Sun Studio 12:Fortran 编程指南

第 11 章 C-Fortran 接口

本章论述 Fortran 与 C 的互操作性方面的问题,内容仅适用于 Sun Studio Fortran 95 和 C 编译器的特定情况。

11.9 Fortran 2003 与 C 的互操作性简要说明了 Fortran 2003 标准第 15 部分中提到的 C 绑定功能。(此标准可以从国际 Fortran 标准 Web 站点 http://www.j3-fortran.org 获得)。Fortran 95 编译器实现了标准中所述的这些功能。

如不特别注明,32 位 x86 处理器视为与 32 位 SPARC 处理器等同。对于 64 位 x86 处理器和 64 位 SPARC 处理器也是如此,只是 x86 系统未定义 REAL*16 和 COMPLEX*32 数据类型,这些数据类型只能用于 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 缺省通过将子程序名转换成小写来忽略大小写。除了字符串常量以外,它会将所有大写字母都转换成小写字母。

对于大/小写问题,有两种常用解决方案:

只能采用这两种解决方案中的一种,不能同时采用。

本章大多数示例的 C 函数名均采用小写字母,并且没有使用 f95-U 编译器选项。

11.1.4 例程名中的下划线

Fortran 编译器通常会在入口点定义和调用中都出现的子程序名末尾追加一个下划线 (_)。该惯例不同于具有相同的用户指定名称的 C 过程或外部变量。几乎所有 Fortran 库过程名都有两个前导下划线,以减少与用户指定的子例程名的冲突。

对于下划线问题,有三种常用解决方案:

只能使用上述解决方案中的一种。

本章的示例都可以使用 BIND(C) 属性声明来避免下划线。BIND(C) 声明可从 Fortran 调用的 C 外部函数,以及可从 C 中作为参数调用的 Fortran 例程。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 数组总是从 0 开始,而 Fortran 数组在缺省情况下是从 1 开始。有两种常用的索引处理方法。


      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]

这对于一维数组不存在任何问题。但对于多维数组,应注意下标在所有引用和声明中是如何出现和使用的-可能需要做些调整。

例如,在 C 中进行部分矩阵操作,而后在 Fortran 中完成余下部分,这样做可能会产生混淆。最好是将整个数组传递给另一语言中的例程,然后在该例程中执行所有矩阵操作,以避免在 C 和 Fortran 中各执行部分操作的情况。

11.1.8 文件描述符和 stdio

Fortran I/O 通道采用的是单元号。底层 SunOS 操作系统不处理单元号,而是处理文件描述符。Fortran 运行时系统会不断变换,所以大多数 Fortran 程序没必要识别文件描述符。

许多 C 程序都使用一组称为标准 I/O(即 stdio)的子例程。有许多 Fortran I/O 函数也使用标准 I/O,而后者又使用操作系统 I/O 调用。下表列出了这些 I/O 系统的某些特性。

表 11–2 Fortran 与 C 之间的 I/O 比较

 

Fortran 单元 

标准 I/O 文件指针 

文件描述符 

文件打开 

为读写打开 

为读打开、为写打开、为读写打开,或者为追加打开;请参见 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。由于 setlocalelibc 以静态方式链接时不起作用,因此只有以动态方式链接了 libc 的 Fortran 程序才能进行国际化。

库中 init 例程的源代码如下


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

f90_initf95 主程序调用。参数分别被设置为 argcargvenvp 的地址。

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

11.4 按值传递数据参数

从 C 中调用时,Fortran 95 程序应在伪参数中使用 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 例程,应该在接口块中包含 !$PRAGMA IGNORE_TKR I,以防止编译器在实际参数和伪参数之间要求类型、类别和等级匹配。

对于传统 Fortran 77,按值调用仅对简单数据可用,并且只能为调用 C 例程的 Fortran 77 例程所用。无法做到让 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.          x and y from Fortran
1.000000, 0.000000  x and y from C
2.000000, 1.000000  new x and y from C
1.00000 1.00000     new x and y from Fortran

11.5 返回值的函数

返回 BYTE、INTEGER、REAL、LOGICAL、DOUBLE PRECISION 或 REAL*16 类型值的 Fortran 函数与返回兼容类型的 C 函数是等同的(请参见表 11–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 返回 COMPLEX 数据

COMPLEX 数据的互操作性情况在 SPARC 32 位和 64 位实现之间有所不同。

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 返回 COMPLEX 数据的函数(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 值: 在 %f0%f1 中返回 COMPLEXDOUBLE COMPLEX,在 %f0%f1%f2%f3 中返回 COMPLEX*32。对于 64 位 SPARC,返回结构(其字段均为浮点型)的 C 函数将在浮点寄存器中返回该结构,但条件是最多需要 4 个这样的寄存器进行此操作。在 64 位 SPARC 平台上,Fortran 函数及其相应的 C 函数的一般样式如下:

Fortran 函数 

C 函数 

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

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

表 11–14 返回 COMPLEX 数据的函数(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 函数等同于具有两个附加第一参数(数据地址和串长度)的 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 )
{ /* return n copies of arg */
  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);
/* make n copies of ch in sbf
*/
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 例程必须在列表(字符参数的长度)末尾提供两个额外的初始参数(指向结果字符串和串长度的指针)和一个附加参数。请注意,在从 C 中调用的 Fortran 例程中,需要显式添加一个末尾空字符。缺省情况下,Fortran 字符串不以空字符终结。

11.6 带标号的 COMMON

可以在 C 中使用全局 struct 来模拟 Fortran 带标号的 COMMON。

表 11–16 模拟带标号的 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 之间共享 I/O

不推荐混合使用 Fortran I/O 和 C I/O(同时从 C 和 Fortran 例程发出 I/O 调用)。最好是全部执行 Fortran I/O 或全部执行 C I/O,而不是两者同时使用。

Fortran I/O 库大部分是在 C 标准 I/O 库之上实现的。Fortran 程序中的每一个打开单元都有相关联的标准 I/O 文件结构。对于 stdinstdoutstderr 流,不需要显式引用该文件结构,所以可以共享它们。

如果 Fortran 主程序调用 C 来执行 I/O,Fortran I/O 库必须在程序启动时进行初始化,以便将单元 0、5 和 6 分别连接到 stderrstdinstdout。要对打开的文件描述符执行 I/O,C 函数必须考虑 Fortran I/O 环境。

请记住:即使主程序在 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 获得)提供了一种从 Fortran 95 程序中引用 C 编程语言定义的过程和全局变量的方法。反过来,它又提供了一种定义 Fortran 子程序或全局变量的方法,从而可以从 C 过程中引用它们。

根据设计,采用这些功能实现 Fortran 95 与 C 程序间的互操作性,可确保符合标准的平台间的可移植性。

Fortran 2003 为派生类型提供了 BIND 属性,并且提供了 ISO_C_BINDING 内在模块。利用此模块,可以访问 Fortran 程序的某些支持可互操作对象规范的命名常量、派生类型和过程。可从 Fortran 2003 标准获取详细信息。