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 から始まることに注意してください。