大多数 C-Fortran 接口必须在以下这些方面全部保持一致:
函数和子例程的定义及调用
数据类型的兼容性
参数传递(按引用或按值)
参数的顺序
过程名(大写、小写或带有结尾下划线 (_))
向链接程序传递正确的库引用
某些 C-Fortran 接口还必须符合:
数组索引及顺序
文件描述符和 stdio
文件权限
函数一词在 C 和 Fortran 中有不同的含义。根据具体情况做出选择很重要:
当 Fortran 例程调用 C 函数时:
如果被调用的 C 函数返回一个值,则将其作为函数从 Fortran 中调用。
如果被调用的 C 函数不返回值,则将其作为子例程调用。
当 C 函数调用 Fortran 子程序时:
如果被调用的 Fortran 子程序是一个函数,则将其作为一个返回兼容数据类型的函数从 C 中调用。
如果被调用的 Fortran 子程序是一个子例程,则将其作为一个返回 int(与 Fortran INTEGER*4 兼容)或 void 值的函数从 C 中调用。如果 Fortran 子例程使用交替返回,则会返回一个值,这种情况下它是 RETURN 语句中的表达式的值。如果 RETURN 语句中没有出现表达式,但在 SUBROUTINE 语句中声明了交替返回,则会返回零。
表 11–2 总结了 Fortran 95(与 C 比较)数据类型的数据大小和缺省对齐。该表假设未应用影响对齐或提升缺省数据大小的编译选项。请注意以下事项:
C 数据类型 int、long int 和 long 在 32 位环境下是等同的(4 字节)。但是,在 64 位环境下 long 和指针为 8 字节。这称为 LP64 数据模型。
在 64 位 SPARC 环境下,当用任意 -m64 选项进行编译时,REAL*16 和 COMPLEX*32 与 16 字节边界对齐。
标有 4/8 的对齐表示缺省情况下与 8 字节边界对齐,但在 COMMON 块中与 4 字节边界对齐。COMMON 中的最大缺省对齐为 4 字节。当用 -m64 选项进行编译时,4/8/16 表示与 16 字节边界对齐。
REAL(KIND=16)、REAL*16、COMPLEX(KIND=16)、COMPLEX*32 只能用于 SPARC 平台。
数组和结构的元素及字段必须兼容。
不能按值传递数组、字符串或结构。
可以在调用点使用 %VAL(arg),按值将参数从 Fortran 95 例程传递到 C 例程。假如 Fortran 例程具有一个显式接口块,该接口块用 VALUE 属性声明了伪参数,则可以按值将参数从 C 传递到 Fortran 95。
数值序列类型的组件的对齐方式与通用块的对齐方式相同,也会受到 -aligncommon 选项的影响。数值序列类型是这样一种序列类型:其中所有组件的类型为缺省整数、缺省实数、双精度实数、缺省复数或缺省逻辑,而不是指针。
在大多数情况下,非数值序列类型的数据类型组件以自然对齐的方式对齐,但 QUAD 变量除外。对于四精度变量,32 位 SPARC 平台和 64 位 SPARC 平台之间的对齐方式不同。
在所有平台上,用 BIND(C) 属性定义的 VAX 结构和数据类型的组件始终与 C 结构具有相同的对齐方式。
Fortran 95 数据类型 |
C 数据类型 |
大小 |
对齐 |
|
---|---|---|---|---|
BYTE x |
char x |
1 |
1 |
|
CHARACTER x |
unsigned char x ; |
1 |
1 |
|
CHARACTER (LEN=n) x |
unsigned char x[n] ; |
n |
1 |
|
COMPLEX x |
struct {float r,i;} x; |
8 |
4 |
|
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; |
8 16 32 |
4 4/8 4/8/16 |
|
DOUBLE COMPLEX x |
struct {double dr, di;} x; |
16 |
4/8 |
|
DOUBLE PRECISION x |
double x ; |
8 |
4 |
|
REAL x |
float x ; |
4 |
4 |
|
REAL (KIND=4) x REAL (KIND=8) x REAL (KIND=16) x (SPARC) |
float x ; double x ; long double x ; |
4 8 16 |
4 4/8 4/8/16 |
|
INTEGER x |
int x ; |
4 |
4 |
|
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; |
1 2 4 8 |
4 4 4 4 |
|
LOGICAL x |
int x ; |
4 |
4 |
|
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; |
1 2 4 8 |
4 4 4 4 |
C 和 Fortran 在区分大小写方面采取截然相反的处理方法:
C 区分大小写-大小写很重要。
Fortran 在缺省情况下忽略大小写。
f95 缺省通过将子程序名转换成小写来忽略大小写。除了字符串常量以外,它会将所有大写字母都转换成小写字母。
对于大/小写问题,有两种常用解决方案:
在 C 子程序中,使 C 函数名全为小写。
用 -U 选项编译 Fortran 程序,该选项会通知编译器保留函数/子程序名称的现有大/小写区别。
只能采用这两种解决方案中的一种,不能同时采用。
本章大多数示例的 C 函数名均采用小写字母,并且没有使用 f95-U 编译器选项。
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 函数不需要在函数名末尾追加下划线。
通常,Fortran 例程按引用传递参数。在调用中,如果非标准函数 %VAL() 中包含一个参数,则调用例程会按值传递该参数。
Fortran 95 按值传递参数的标准方法是通过 VALUE 属性和 INTERFACE 块。请参见 11.4 按值传递数据参数。
C 通常按值传递参数。如果在参数前加上表示“和”的符号 (&),C 会使用指针按引用传递参数。C 总是按引用传递数组和字符串。
除字符串参数之外,Fortran 和 C 均以相同的顺序传递参数。但对于每个字符型参数,Fortran 例程都会传递一个附加参数,用以指定串长度。这些参数在 C 中是 long int 数量,按值进行传递。
参数顺序为:
与每个参数相应的地址(数据或函数)
与每个字符参数对应的 long int(字符串长度的完整列表位于其他参数的完整列表之后)
示例:
Fortran 代码片段: |
等价的 C 代码片段: |
||
---|---|---|---|
|
|
C 数组总是从 0 开始,而 Fortran 数组在缺省情况下是从 1 开始。有两种常用的索引处理方法。
如上述示例所示,可以使用 Fortran 缺省设置。此时,Fortran 元素 B(2) 等同于 C 元素 b[1]。
可以指定 Fortran 数组 B 以 B(0) 开始,如下所示:
INTEGER B(0:2) |
这样,Fortran 元素 B(1) 就等同于 C 元素 b[1]。
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 中各执行部分操作的情况。
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 间的整数 |
要链接正确的 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% |