Sun Studio 12: Fortran プログラミングガイド

第 11 章 C と Fortran のインタフェース

この章では、Fortran と C の相互運用性に関する問題を取り上げます。ここで説明する内容は、Sun Studio Fortran 95 と C コンパイラの仕様のみに該当します。

「11.9 Fortran 2003 と C の相互運用性」では、Fortran 2003 規格草案の第 15 節で提案されている C とのリンク機能について簡単に説明します。この規格草案は、Web サイト http://www.j3-fortran.org で入手できます。95 コンパイラは、規格に記述されているとおりに、これらの機能を実装しています。

特に注記がある場合を除き、32 ビット x86 プロセッサは 32 ビット SPARC プロセッサと同様に取り扱います。64 ビット x86 と 64 ビット SPARC も同じです。ただし例外として、REAL*16 および COMPLEX*32 データ型は x86 システム用に定義されておらず、SPARC のみで利用可能です。

11.1 互換性について

ほとんどの C と Fortran のインタフェースでは、次に示すことを正しく理解しておく必要があります。

また、一部の C と Fortran のインタフェースでは、次に示すことを正しく理解しておく必要があります。

11.1.1 関数とサブルーチン

「関数」という言葉の意味は、C と Fortran では異なります。 状況によって、どちらの意味で解釈するかが重要です。

Fortran ルーチンから C 関数を呼び出す場合

C 関数から Fortran 副プログラムを呼び出す場合

11.1.2 データ型の互換性

表 11–2 では、Fortran 95 のデータ型と C のデータ型のデータサイズとデフォルトの境界整列を比較しています。ここでは、境界に影響したり、適用されるデフォルトのデータサイズを昇格させたりするコンパイルオプションを指定しないものとします。次の点に注意してください。

表 11–1 データサイズと境界 — (バイト数での) 参照渡し (f95cc)

Fortran 95 のデータ型 

C のデータ型 

サイズ 

境界 

BYTE x

char x

CHARACTER x

unsigned char x ;

 

CHARACTER (LEN=n) x

unsigned char x[n] ;

n

COMPLEX x

struct {float r,i;} x;

 

COMPLEX (KIND=4) x

COMPLEX (KIND=8) x

COMPLEX (KIND=16) x (SPARC)

struct {float r,i;} x;

struct {double dr,di;} x;

struct {long double, dr,di;} x;

16 

32 

4/8 

4/8/16 

DOUBLE COMPLEX x

struct {double dr, di;} x;

16 

4/8 

DOUBLE PRECISION x

double x ;

REAL x

float x ;

 

REAL (KIND=4) x

REAL (KIND=8) x

REAL (KIND=16) x (SPARC)

float x ;

double x ;

long double x ;

16 

4/8 

4/8/16 

INTEGER x

int x ;

 

INTEGER (KIND=1) x

INTEGER (KIND=2) x

INTEGER (KIND=4) x

INTEGER (KIND=8) x

signed char x ;

short x ;

int x ;

long long int x;

LOGICAL x

int x ;

 

LOGICAL (KIND=1) x

LOGICAL (KIND=2) x

LOGICAL (KIND=4) x

LOGICAL (KIND=8) x

signed char x ;

short x ;

int x ;

long long int x;

11.1.3 大文字と小文字

C と Fortran では、字種 (大文字/小文字) に関する扱いが異なります。

f95 のデフォルトでは、副プログラム名を小文字に変換して、字種を無視します。 つまり、文字列定数の中を除き、すべての大文字を小文字に変換します。

大文字と小文字に関する問題には、一般に次のような 2 つの解決策があります。

前述の 2 つの解決策のどちらか 1 つを使用してください。 両方を使用してはいけません。

この章の例のほとんどは、C の関数名に小文字だけを使用しています。f95-U コンパイラオプションは使用していません。

11.1.4 ルーチン名の下線

Fortran コンパイラは、通常、入口定義と呼び出しの両方に現れる副プログラムの名前に下線 (_) を追加します。これによって、ユーザー割り当て名が同じである C の手続きや外部変数と区別します。Fortran ライブラリのほとんどすべての手続き名には、ユーザーが割り当てるサブルーチンとの競合を減らすため、先頭に 2 つの下線が付けられています。

下線に関する問題には、一般に次の 3 つの解決策があります。

これらの中のいずれか 1 つを使用してください。

この章の例は、BIND(C) 属性宣言を使用して、下線をなくしています。 BIND(C) は、C の外部関数が Fortran から呼び出し可能なこと、また Fortran ルーチンが引数として C から呼び出し可能なことを宣言します。Fortran は、通常、外部名に対して行うようには下線を付加しません。 BIND(C) は、そうした参照を含むそれぞれの副プログラム内で使用する必要があります。使用規則は次のとおりです。


       FUNCTION ABC
        EXTERNAL XYZ
        BIND(C) ABC, XYZ

この例では、XYZ が外部 C 関数であることばかりでなく、Fortran の呼び出し側の ABC を C 関数から呼び出し可能であると宣言しています。BIND(C) を使用すると、C の関数の方で名前に下線を追加する必要はありません。

11.1.5 引数の参照渡しと値渡し

一般的には、Fortran ルーチンは引数を参照で渡します。呼び出し時に、非標準関数の %VAL() に引数を入れると、呼び出し元のルーチンはその引数を値で渡します。

Fortran 95 で引数を値で渡す場合の標準的な方法は、VALUE 属性と INTERFACE ブロックを使用する方法です。「11.4 データ引数の値渡し」を参照してください。

一般的には、C は引数を値で渡します。引数の前にアンパサンド記号 (&) を付けた場合は、C はその引数を、ポインタを使用して参照で渡します。配列と文字列に関しては、C でも常に参照で渡します。

11.1.6 引数の順序

文字列の引数の場合を除くと、Fortran と C は引数を同じ順序で渡します。ただし、各文字列引数については、Fortran ではさらに文字列の長さを示す引数も渡します。文字列長は、値で渡される C の long int の量と同じです。

引数の順序は次のとおりです。

例:

Fortran コードの一部 

対応する C のコード 


CHARACTER*7 S
INTEGER B(3)
...
 CALL SAM( S, B(2) )

char s[7];
int b[3];
...
sam_( s, &b[1], 7L ) ; 

11.1.7 配列の添字付けと順番

配列の添字付けと順番については Fortran と C とでは異なります。

11.1.7.1 配列の添字付け

C の配列は常にゼロから始まりますが、Fortran の配列はデフォルトでは 1 から始まります。この問題には、次のような 2 つの解決策があります。


      INTEGER B(0:2)

このときは、Fortran の要素 B(1) が C の b[1] 要素と同義になります。

11.1.7.2 配列の順番

Fortran の配列は列主導の順番で、A(3,2) のように格納されます。


A(1,1)  A(2,1)  A(3,1)  A(1,2)  A(2,2)  A(3,2)

C の配列は行主導の順番で、A[3][2] のように格納されます。


A[0][0] A[0][1] A[1][0] A[1][1] A[2][0] A[2][1]

1 次元の配列では、これによって問題は生じません。しかし、多次元の配列では、すべての参照と宣言における添字の順番と使用法に気をつけてください。 なんらかの調整が必要になります。

たとえば、行列操作の一部を C で行い、残りを Fortran で行うのは混乱が生じる可能性があります。一方の言語で全体の配列をルーチンに渡し、そのルーチン内ですべての行列操作を実行すれば、混乱を避けることができます。

11.1.8 ファイル記述子と stdio

Fortran の入出力チャネルは、装置番号で表されます。プラットフォームとして使用している SunOS オペレーティングシステムは、装置番号ではなく、ファイル記述子を扱います。Fortran の実行時のシステムが装置番号からファイル記述子に変換するので、ほとんどの Fortran プログラムはファイル記述子を認識する必要はありません。

C プログラムの多くは、標準入出力 (stdio) と呼ばれるサブルーチンセットを使用しています。Fortran の入出力関数の多くは標準入出力を使用しており、これはオペレーティングシステムの入出力呼び出しを使用しています。このような入出力システムの特性の一部を次の表に示します。

表 11–2 Fortran と C の入出力の比較

 

Fortran 装置 

標準入出力のファイルポインタ 

ファイル記述子 

ファイルを開く 

読み書き用に開く 

読み取り用、書き込み用、読み書き両用、または追加用に開く。open(2) 参照

読み取り用、書き込み用、または読み書き両用に開く。 

属性 

書式付き、書式なし 

常に書式なし、ただし、書式解釈ルーチンによる読み書きは可能。 

常に書式なし 

探査 

直接、順番 

物理ファイルの表現が直接探査の場合は直接探査、ただし、常に順番に読み取り可。 

物理ファイルの表現が直接探査の場合は直接探査、ただし、常に順番に読み取り可。 

構造 

レコード 

バイトストリーム 

バイトストリーム 

形式 

任意の負でない 0 から 2147483647 までの整数 

ユーザーのアドレス空間における構造体へのポインタ 

0 から 1023 までの整数 

11.1.9 ライブラリと f95 コマンドでのリンク

適切な Fortran および C ライブラリをリンクするためには、f95 コマンドを使用して、リンカーを起動します。

例 1: コンパイラを使用してリンクします。


demo% cc -c someCroutine.c
demo% f95 theF95routine.f someCroutine.o  <- このコマンド行でリンクを実行
demo% a.out
 4.0 4.5
 8.0 9.0
demo%

11.2 Fortran 初期化ルーチン

f95 によりコンパイルされた主プログラムでは、プログラム起動時にライブラリのダミー初期化ルーチン f90_init を呼び出します。ライブラリのルーチンは、ダミーであり、何も処理しません。コンパイラにより生成される呼び出しでは、プログラムの引数と環境へのポインタが渡されます。これらの呼び出しにより、各自の C 言語ルーチンでプログラムの起動前に独自の方法でプログラムを初期化するときに使用できるソフトウェアのフックが提供されます。

このような初期化ルーチンの使用法の一つに、国際化された Fortran プログラムに対して setlocale を呼び出す方法があります。setlocale は、libc が静的にリンクされている場合機能しないので、libc に動的にリンクされる Fortran プログラムだけが国際化できます。

ライブラリの init ルーチンのソースコードは、次のとおりです。


void f90_init(int *argc_ptr, char ***argv_ptr, Char ***envp_ptr) {}

f90_init は、f95 主プログラムにより呼び出されます。各引数には、argc のアドレス、argv のアドレス、envp のアドレスが設定されます。

11.3 データ引数の参照渡し

Fortran ルーチンと C 手続きとの間でデータを渡す標準的な方法は、参照渡しです。C の手続きから見ると、Fortran の副プログラムまたは関数呼び出しは、すべての引数をポインタで表す手続き呼び出しのようになります。唯一異なる点は、Fortran が文字列と関数を引数として扱う方法と、CHARACTER*n 関数の戻り値として扱う方法です。

11.3.1 単純なデータ型

単純なデータ型の場合 (COMPLEX または CHARACTER 文字列以外)、次に示すように、C ルーチンにおいてそれぞれ関連する引数をポインタにより定義するか、または渡します。

表 11–3 単純型データを渡す

Fortran が C を呼び出す 

C が Fortran を呼び出す 


integer i
real r
external CSim
 i = 100
 call CSim(i,r)
...

----------------------------

void csim_(int *i, float *r)
{
  *r = *i;
}

int i=100;
float r;
extern void fsim_(int *i, float *r);
fsim_(&i, &r);
...

------------------------------

subroutine FSim(i,r)
  integer i
  real r
  r = i
  return
end 

11.3.2 複素数データ

Fortran の複素数データ項については、2 つの float または 2 つの double からなる 1 つの C の構造体へのポインタとして渡します。

表 11–4 複素数データを渡す

Fortran が C を呼び出す 

C が Fortran を呼び出す 


complex w
double complex z
external CCmplx
call CCmplx(w,z)
...

------------------------------

struct cpx {float r, i;};
struct dpx {double r,i;};
void ccmplx_(
   struct cpx *w,
   struct dpx *z)
{
    w -> r = 32.;
    w -> i = .007;
    z -> r = 66.67;
    z -> i = 94.1;
} 

struct cpx {float r, i;};
struct cpx d1;
struct cpx *w = &d1
struct dpx {double r, i;};
struct dpx d2;
struct dpx *z = &d2
fcmplx_( w, z );
...

---------------

subroutine FCmplx( w, z )
  complex w
  double complex z
  w = (32., .007)
  z = (66.67, 94.1)
  return
end 

64 ビット環境では、COMPLEX 値がレジスタに戻されま す。

11.3.3 文字列

C と Fortran ルーチンとの間で文字列を渡すことは推奨できません。 これは、標準的なインタフェースがないからです。ただし、次を考慮してください。

次の例で、文字列を引数とする Fortran 呼び出しを、対応する C のコードとともに示します。

表 11–5 CHARACTER 文字列を渡す

Fortran が C を呼び出す 

C に対応する Fortran のコード 


CHARACTER*7 S
INTEGER B(3)
...
CALL CSTRNG( S, B(2) )
... 

char s[7];
int b[3];
...
cstrng_( s, &b[1], 7L );
... 

文字列の長さが呼び出されたルーチンで必要なければ、追加の引数は無視されます。ただし、Fortran では C のように明示的なヌル文字で文字列を自動的に終了させません。これは、呼び出し側のプログラムで追加する必要があります。

文字配列への呼び出しは、単一文字変数への呼び出しと似ています。配列の開始アドレスが渡され、それが使用する長さが配列の単一要素の長さになります。

11.3.4 1 次元配列

C では配列の添字が 0 で始まります。

表 11–6 1 次元配列を渡す

Fortran が C を呼び出す 

C が Fortran を呼び出す 


integer i, Sum
integer a(9)
external FixVec
...
call FixVec ( a, Sum )
...

------------------------------

void fixvec_ (
int v[9], int *sum )
{
   int i;
   *sum = 0;
   for ( i = 0; i <= 8; i++ )
      *sum = *sum + v[i];
} 

extern void vecref_
       ( int[], int * );
...
int i, sum;
int v[9] = ...
vecref_( v, &sum );
...

------------------------------

subroutine VecRef( v, total)
  integer i, total, v(9)
  total = 0
  do i = 1,9
    total = total + v(i)
  end do
  ... 

11.3.5 2 次元配列

C と Fortran とでは行と列が入れ替わります。

表 11–7 2 次元配列を渡す

Fortran が C を呼び出す 

C が Fortran を呼び出す 


REAL Q(10,20)
...
Q(3,5) = 1.0
CALL FIXQ(Q)
...

------------------------------

void fixq_( float a[20][10] )
{
   ...
   a[5][3] = a[5][3] + 1.;
   ...
}

extern void
     qref_( int[][10], int *);
...
 int m[20][10] = ... ;
 int sum;
...
 qref_( m, &sum );
...

------------------------------

SUBROUTINE QREF(A,TOTAL)
  INTEGER A(10,20), TOTAL
  DO I = 1,10
    DO J = 1,20
      TOTAL = TOTAL + A(I,J)
    END DO
  END DO
  ...

11.3.6 構造体

C と Fortran 95 の構造型は、対応する成分間に互換性があれば、それぞれのルーチン間で相互に受け渡しできます。f95 は、古い STRUCTURE 文を受け付けます。

表 11–8 古い FORTRAN 77 の STRUCTURE レコードを渡す

Fortran が C を呼び出す 

C が Fortran を呼び出す 


STRUCTURE /POINT/
REAL X, Y, Z
END STRUCTURE
RECORD /POINT/ BASE
EXTERNAL FLIP
...
CALL FLIP( BASE )
...

------------------------------

struct point {
    float x,y,z;
};
void flip_( struct point *v )
{
    float t;
    t = v -> x;
    v -> x = v -> y;
    v -> y = t;
    v -> z = -2.*(v -> z);
}

struct point {
    float x,y,z;
};
void fflip_ ( struct point *) ;
...
struct point d;
struct point *ptx = &d;
...
fflip_ (ptx);
...

------------------------------

SUBROUTINE FFLIP(P)
  STRUCTURE /POINT/
    REAL X,Y,Z
  END STRUCTURE
  RECORD /POINT/ P
  REAL T
  T = P.X
  P.X = P.Y
  P.Y = T
  P.Z = -2.*P.Z
  ...

Fortran (VAX) 77 構造体は、どのプラットフォームでもつねに C の構造体と同じ境界整列になります。ただし、この境界整列はプラットフォーム間で異なります。

表 11–9 Fortran 95 構造体を渡す

Fortran 95 が C を呼び出す 

C が Fortran 95 を呼び出す 


TYPE point
  SEQUENCE
  REAL :: x, y, z
END TYPE point
TYPE (point) base
EXTERNAL flip
...
CALL flip( base)
...

------------------------------

struct point {
    float x,y,z;
};
void flip_( struct point *v )
{<
    float t;
    t = v -> x;
    v -> x = v -> y;
    v -> y = t;
    v -> z = -2.*(v -> z);
}

struct point {
  float x,y,z;
};
extern void fflip_ (
     struct point *) ;
...
struct point d;
struct point *ptx = &d;
...
fflip_ (ptx);
...

------------------------------

SUBROUTINE FFLIP( P )
    TYPE POINT
     SEQUENCE
     REAL :: X, Y, Z
    END TYPE POINT
    TYPE (POINT) P
    REAL :: T
    T = P%X
    P%X = P%Y
    P%Y = T
    P%Z = -2.*P%Z
    ...

Fortran 95 規格では、記憶領域の並び順がコンパイルによって保持されるように、構造型の定義に SEQUENCE 文を含めなければならないことになっています。

どのプラットフォームでも、デフォルトでは、数値シーケンス型の要素はワード (4 バイト) 境界に整列されます。これは、x86 プラットフォームでも C の構造体の境界整列と同じですが、SPARC V8 および V9 プラットフォームでの C 境界整列と異なります。数値シーケンス型の境界整列を C の構造体の整列に一致させるには、-aligncommon オプションを使用します。32 ビットの SPARC の C 構造体と一致させるには -aligncommon=8、64 ビットの SPARC の C 構造体と一致させるには -aligncommon=16 を使用してください。

SEQUENCE を使って明示的に宣言されていない構造型は、SPARC V8 および V9 プラットフォームでは C の構造体と同じ境界整列になりますが、x86 プラットフォームでは異なります。コンパイラオプションを使って、この整列を変更することはできません。

11.3.7 ポインタ

FORTRAN 77 (Cray) のポインタは、ポインタへのポインタとして C ルーチンに渡すことができます。 これは、Fortran ルーチンが引数を参照で渡すからです。

表 11–10 FORTRAN 77 (Cray) の POINTER を渡す

Fortran が C を呼び出す 

C が Fortran を呼び出す 


REAL X
POINTER (P2X, X)
EXTERNAL PASS
P2X = MALLOC(4)
X = 0.
CALL PASS(P2X)
...

------------------------------

void pass_(p)
> float **p;
{
  **p = 100.1;
}

extern void fpass_( float** );
...
float *p2x;
...
 fpass_(&p2x) ;
...

------------------------------

SUBROUTINE FPASS (P2X)
  REAL X
  POINTER (P2X, X)
  X = 0.
  ...

C ポインタは Fortran 95 のスカラーポインタとは互換性がありますが、配列ポインタとは互換性がありません。

Fortran 95 がスカラーポインタで C を呼び出す 

Fortran 95 ルーチン:


INTERFACE
  SUBROUTINE PASS(P)
   REAL, POINTER :: P
  END SUBROUTINE
END INTERFACE

 REAL, POINTER :: P2X
 ALLOCATE (P2X)
 P2X = 0
 CALL PASS(P2X)
 PRINT*, P2X
END

C ルーチン


void pass_(p);
float **p;
{
  **p = 100.1;
}

 

Cray ポインタと Fortran 95 ポインタの主な違いは、Cray ポインタには常にターゲットが指定されることです。多くの場合、Fortran 95 ポインタを宣言すると、ターゲットは自動的に特定されます。また、呼び出された側の C ルーチンにも明示的な INTERFACE ブロックが必要です。

配列または部分配列への Fortran 95 ポインタを渡すには、次の例のように特定の INTERFACE ブロックが必要です。


Fortran 95 ルーチン
INTERFACE
  SUBROUTINE S(P)
    integer P(*)
  END SUBROUTINE S
END INTERFACE
integer, target:: A(0:9)
integer, pointer :: P(:)
P => A(0:9:2) !! ポインタは A の要素を 1 つおきに選択
call S(P)
...

C ルーチン
void s_(int p[])
{
   /* 中央の要素を変更する */
   p[2] = 444;
}

C ルーチン S は Fortran 95 ルーチンでありません。このため、INTERFACE ブロックでは、想定された形 (integer P(:)) でルーチン S を定義することはできません。C ルーチンが配列の実際のサイズを知る必要がある場合は、配列を引数として C ルーチンに渡す必要があります。

C と Fortran では添字付けの方法が異なり、C 配列の添字は 0 から始まることに注意してください。

11.4 データ引数の値渡し

Fortran 95 のプログラムでは、C から呼び出されるときに仮引数の VALUE 属性を使用し、Fortran 95 から呼び出す C ルーチンのために INTERFACE ブロックを追加する必要があります。

表 11–11 C と Fortran 95 の間で単純なデータ要素を渡す

Fortran 95 が C を呼び出す 

C が Fortran 95 を呼び出す 


PROGRAM callc
INTERFACE 
 INTEGER FUNCTION crtn(I)
  BIND(C) crtn 
  INTEGER, VALUE, INTENT(IN) :: I 
 END FUNCTION crtn
END INTERFACE 
M = 20 
MM = crtn(M) 
WRITE (*,*) M, MM
END PROGRAM

---------------------------------

int crtn(int x)
{
  int y;
  printf("%d input \n", x);
  y = x + 1;
  printf("%d returning \n",y);
  return(y);
}

---------------------------------

Results:
20 input
21 returning
20 21

#include <stdlib.h>
int main(int ac, char *av[]) {
  to_fortran_(12);
}

--------------------------------

SUBROUTINE to_fortran(i)
  INTEGER, VALUE :: i
  PRINT *, i
END

C ルーチンを実引数として別のデータ型で呼び出す場合は、INTERFACE ブロックに !$PRAGMA IGNORE_TKR I を含めることによって、実引数と仮引数の型、種類、ランクの一致をコンパイラから要求されないようにします。

古い FORTRAN 77 での値による呼び出しは、単純型のデータでだけ、FORTRAN 77 ルーチンが C のルーチンを呼び出す場合にかぎり使用できました。C のルーチンから FORTRAN 77 ルーチンを呼び出して、値で引数を渡す方法はありません。配列、文字列、構造体は、参照で渡すようにしてください。

FORTRAN 77 ルーチンから C ルーチンに値を渡すには、非標準の Fortran 関数 %VAL(arg) を呼び出しの中で引数として使用してください。

次の例では、FORTRAN 77 ルーチンが値により x を渡し、参照により y を渡しています。C のルーチンは x と y を増分しましたが、y だけが変更されます。

Fortran が C を呼び出す 

Fortran ルーチン:


REAL x, y
x = 1.
y = 0.
PRINT *, x,y
CALL value( %VAL(x), y)
PRINT *, x,y
END

C ルーチン


void value_( float x, float *y)
{
  printf("%f, %f\n",x,*y);
  x = x + 1.;
  *y = *y + 1.;
  printf("%f, %f\n",x,*y);
}

コンパイルと実行による出力:


1.00000 0.          Fortran から x と y を入力
1.000000, 0.000000  C から x と y を入力
2.000000, 1.000000  C から新しい x と y を入力
1.00000 1.00000     Fortran から新しい x と y を入力

11.5 値を戻す関数

BYTE、INTEGER、REAL、LOGICAL、DOUBLE PRECISION、または REAL*16 型の値を戻す Fortran 関数は、互換性のある型を戻す C の関数と等価です (表 11–1 を参照)。文字関数の戻り値には引数が 2 つ追加され、複素数関数の戻り値には引数が 1 つ追加されます。

11.5.1 単純型データを戻す

次の例は REAL または float 値を戻します。BYTE、INTEGER、LOGICAL、DOUBLE PRECISION、および REAL*16 も同じような方法で扱われます。

表 11–12 REAL または float の値を戻す関数

Fortran が C を呼び出す 

C が Fortran を呼び出す 


real ADD1, R, S
external ADD1
R = 8.0
S = ADD1( R )
...

------------------------------

float add1_( pf )
float *pf;
{
   float f ;
   f = *pf;
   f++;
   return ( f );
}

float r, s;
extern float fadd1_() ;
r = 8.0;
s = fadd1_( &r );
...

------------------------------

real function fadd1 (p)
  real p
  fadd1 = p + 1.0
  return
end

11.5.2 複素数データを戻す

複素数データの相互運用性に関する状況は、32 ビットの実装と 64 ビットの SPARC V9 の実装では異なります。

11.5.2.1 32 ビットプラットフォーム

32 ビットプラットフォームで、COMPLEX または DOUBLE COMPLEX を戻す Fortran 関数は、メモリーにある戻り値を指す追加の引数を最初の引数として持つ C の関数と同じです。Fortran 関数と対応する C 関数の一般的な形式は次のとおりです。

Fortran 関数 

C 関数 


COMPLEX FUNCTION CF( a1,a2,...,an)

cf_ (return, a1, a2, ..., an)
 struct { float r, i; } *return

表 11–13 複素数データを戻す関数 (32 ビット SPARC)

Fortran が C を呼び出す 

C が Fortran を呼び出す 


COMPLEX U, V, RETCPX
EXTERNAL RETCPX
U = ( 7.0, -8.0)
V = RETCPX(U)
...

------------------------------

struct complex { float r, i; };
void retcpx_( temp, w )
struct complex *temp, *w;
{
   temp->r = w->r + 1.0;
   temp->i = w->i + 1.0;
   return;
}

struct complex { float r, i; };
struct complex c1, c2;<
struct complex *u=&c1, *v=&c2;
extern retfpx_();
u -> r = 7.0;
u -> i = -8.0;
retfpx_( v, u );
...

------------------------------

COMPLEX FUNCTION RETFPX(Z)
  COMPLEX Z
  RETFPX = Z + (1.0, 1.0)
  RETURN
END

11.5.2.2 64 ビット SPARC プラットフォーム

64 ビット SPARC 環境では、COMPLEX 値が浮動小数点レジスタに戻されます。COMPLEXDOUBLE COMPLEX はそれぞれ %f0%f1 に戻され、COMPLEX*32%f0%f1%f2、および %f3 に戻されます。64 ビット SPARC では、要素がすべて浮動小数点型の構造体を返す C 関数は、浮動小数点レジスタで構造体を返します。ただし、そのために必要なレジスタの数が 4 個以下の場合にかぎられます。Fortran 関数と対応する C 関数の一般的な形式は次のとおりです。

Fortran 関数 

C 関数 

COMPLEX FUNCTION CF(a1, a2, ..., an)

struct {float r,i;} cf_ (a1, a2, ..., an)

表 11–14 複素数データを戻す関数 (64 ビット SPARC)

Fortran が C を呼び出す 


COMPLEX U, V, RETCPX
EXTERNAL RETCPX
U = ( 7.0, -8.0)
V = RETCPX(U)
...

---------------------------------------------------------

struct complex {float r, i; };
struct complex retcpx_(struct complex *w )
{
    struct complex temp;
    temp.r = w->r + 1.0;
    temp.ii = w->i + 1.0;
    return (temp);
}

C が Fortran を呼び出す 


struct complex { float r, i; };
struct complex c1, c2;
struct complex *u=&c1;
extern struct complex retfpx_(struct complex *);
  u -> r = 7.0;
  u -> i = -8.0;
  retfpx_( u );
...

---------------------------------------------------------

COMPLEX FUNCTION RETFPX(Z)
  COMPLEX Z
  RETFPX = Z + (1.0, 1.0)
  RETURN
END

11.5.3 CHARACTER 文字列を戻す

C と Fortran ルーチンの間で文字列を渡すことは推奨できません。ただし、Fortran の文字列の値を持つ関数は、データアドレスとデータ長の 2 つの引数がはじめに追加された C の関数と同じです。Fortran 関数と対応する C 関数の一般的な形式は次のとおりです。

Fortran 関数 

C 関数 

CHARACTER*n FUNCTION C(a1, ..., an)


 
void c_ (result, length, a1, ..., an)
char result[ ];
long length;

次に例を示します。

表 11–15 CHARACTER 文字列を戻す関数

Fortran が C を呼び出す 

C が Fortran を呼び出す 


CHARACTER STRING*16, CSTR*9
STRING = ’ ’
STRING = ’123’ // CSTR(’*’,9)
...

------------------------------

void cstr_( char *p2rslt,
            long rslt_len,
            char *p2arg,
            int *p2n,
            long arg_len )
{ /* 引数に n 個のコピーを戻す */
  int count, i;
  char *cp;
  count = *p2n;
  cp = p2rslt;
  for (i=0; i<count; i++) {
    *cp++ = *p2arg ;
  }
}

void fstr_( char *, long,
           char *, int *, long );
char sbf[9] = "123456789";
char *p2rslt = sbf;
int rslt_len = sizeof(sbf);
char ch = ’*’;
int n = 4;
int ch_len = sizeof(ch);
/* sbf 内に ch のコピーを n 個作成
*/
fstr_( p2rslt, rslt_len,
       &ch, &n, ch_len );
...

------------------------------

FUNCTION FSTR( C, N)
  CHARACTER FSTR*(*), C
  FSTR = ’’
  DO I = 1,N
    FSTR(I:I) = C
  END DO
  FSTR(N+1:N+1) = CHAR(0)
END

この例では、C 関数と呼び出し側の C ルーチンは、リストの最初に 2 つの引数 (結果として戻される文字列へのポインタと文字列長) が、そして、リストの最後に 1 つの追加引数 (文字列引数の長さ) が追加されています。C から呼び出された Fortran ルーチンでは最後のヌル文字を明示的に追加する必要があることに注意してください。Fortran の文字列は、デフォルトでは NULL 終端されません。

11.6 名前付き COMMON

Fortran の名前付き COMMON は、大域的構造体を使用して C の中で代替できます。

表 11–16 Fortran の名前付き COMMON 定義

Fortran の COMMON 定義 

C "COMMON" 定義 


COMMON /BLOCK/ ALPHA,NUM
...

extern struct block {
  float alpha;
  int num;
};
extern struct block block_ ;
main ()
{
...
  block_.alpha = 32.;
  block_.num += 1;
...
}

C ルーチンにより確立された外部名は、Fortran プログラムにより作成されたブロックとリンクさせるために下線で終了しなければいけません。C 指令 #pragma pack は Fortran のときと同じ埋め込みが必要な場合があります。

f95 は、デフォルトで、共通ブロックのデータを最大で 4 バイトの境界に配列します。共通ブロック内のすべてのデータ要素の自然配列取得し、デフォルトの構造配列と一致させるには、Fortran ルーチンをコンパイルする際にオプション -aligncommon=16 を使用します。

11.7 Fortran と C との入出力の共有

Fortran の入出力とC の入出力を混合すること (C ルーチンと Fortran ルーチンの両方から入出力呼び出しを発行すること) は推奨できません。すべて Fortran の入出力で行うか、すべて C の入出力で行うかのどちらかに統一するのが安全です。

Fortran の入出力ライブラリは、大部分が C の標準入出力ライブラリに追加する形で実装されています。Fortran プログラムで開いた装置はすべて、標準入出力のファイル構造と対応付けられています。stdinstdoutstderr のストリームに関しては、ファイル構造を明示的に参照する必要がなく、共有できます。

Fortran の主プログラムが入出力を行うために C を呼び出す場合は、プログラム起動時に Fortran の入出力ライブラリを初期化して、装置 0、5、6 をそれぞれ stderrstdinstdout に接続します。ファイル記述子を開いて入出力を実行する場合、C の関数は Fortran の入出力環境を考慮する必要があります。

たとえ主プログラムが C で書かれていても、f95 でリンクするようにしてください。

11.8 選択戻り (あまり使用されません)

FORTRAN 77 の選択戻り機能はすでに廃止されているので、移植上の問題がなければ使用しないでください。選択戻りに対応する機能は C にはありません。 したがって、問題は C のルーチンが選択戻りを持つ Fortran のルーチンを呼び出す場合だけです。Fortran 95 は FORTRAN 77 の選択戻りを受け付けますが、これを利用することはお勧めしません。

選択戻りは、RETURN 文の式の int 値を戻します。これは実装方式にかなり依存するため、使用しないでください。

表 11–17 選択戻り (あまり使用されません)

C が Fortran を呼び出す 

例の実行 


int altret_ ( int * );
main ()
{
  int k, m ;
  k =0;
  m = altret_( &k ) ;
  printf( "%d %d\n", k, m);
}

------------------------------

SUBROUTINE ALTRET( I, *, *)
  INTEGER I
  I = I + 1
  IF(I .EQ. 0) RETURN 1
  IF(I .GT. 0) RETURN 2
  RETURN
END

demo% cc -c tst.c
demo% f95 -o alt alt.f tst.o
alt.f:
altret:
demo% alt
1 2

C ルーチンは Fortran ルーチンから戻り値 2 を受け取る。これは、RETURN 2 文が実行されたためである。

11.9 Fortran 2003 と C の相互運用性

Fortran 2003 規格の草案 (http://www.j3-fortran.org から入手可能) では、C プログラミング言語で定義された手続きおよび大域的変数を Fortran 95 プログラム内から参照する方法について規定しています。また逆に、C 手続きから参照できるように Fortran 副プログラムや大域的変数を定義する方法も規定しています。

設計上は、Fortran 95 プログラムと C プログラムの相互運用性を実現するこれらの機能を使用すると、規格を満たすプラットフォーム間での移植可能性が保証されます。

Fortran 2000 では、構造型用の BIND 属性と、ISO_C_BINDING 組み込みモジュールを提供しています。このモジュールを使用すると、Fortran プログラムから、相互運用可能なオブジェクトの仕様をサポートしている特定の名前付き定数、構造型、および手続きにアクセスできます。詳細は、Fortran 2003 規格を参照してください。