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

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