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 开始。