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 COMPLEX 数据

将 Fortran COMPLEX 数据项作为指针传递到具有两种浮点或两种双精度数据类型的 C 结构:

表 11–4 传递 COMPLEX 数据类型

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 调用: 


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 一维数组

在 C 中数组下标以 0 开始。

表 11–6 传递一维数组

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 二维数组

C 与 Fortran 间的行列转换。

表 11–7 传递二维数组

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 77 (VAX) 结构与 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 平台上 C 结构的对齐方式。使用 -aligncommon 选项可更改数值序列类型的对齐方式,以便与 C 结构相匹配。使用 -aligncommon=8 匹配 32 位 SPARC C 结构,使用-aligncommon=16 匹配 64 位 SPARC。

未使用 SEQUENCE 显式声明的派生类型与 SPARC 平台上的 C 结构对齐方式相同,但与 x86 平台上的对齐方式不同。这种对齐方式不随编译器选项而改变。

11.3.7 指针

由于 Fortran 例程按引用传递参数,因此可将 FORTRAN 77 (Cray) 指针作为指针的指针传递给 C 例程。

表 11–10 传递 FORTRAN 77 (Cray) 指针

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) !! pointer selects every other element of A
call S(P)
...

C 例程:
void s_(int p[])
{
   /* change middle element */
   p[2] = 444;
}

请注意,由于 C 例程 S 不是 Fortran 95 例程,因此不能在接口块中将其定义成假定的形状 (integer P(:))。如果 C 例程需要知道数组的实际大小,必须将其作为参数传递给 C 例程。

另请注意,C 与 Fortran 间的下标编排不同,C 数组以下标 0 开始。