Who are you?

混合编程注意事项

这篇文档主要对C和Fortran混合编程的注意事项做一个补充。

<!—more—>

函数还是子例程

函数在C和Fortran中有不同的含义:

  • 在 C 中,所有的子程序都是函数(function);但 void 函数不会返回值。
  • 在 Fortran 中,函数(function)会传递一个返回值,但子例程(subroutine)一般不传递返回值。

因此,

  • 当 Fortran 例程调用 C 函数时:
    • 如果被调用的 C 函数返回一个值,则将其作为函数从 Fortran 中调用。
    • 如果被调用的 C 函数不返回值,则将其作为子例程调用。
  • 当 C 函数调用 Fortran 子程序时:
    • 如果被调用的 Fortran 子程序是一个函数,则将其作为一个返回兼容数据类型的函数从 C 中调用。
    • 如果被调用的 Fortran 子程序是一个子例程,则将其作为一个返回 int(与 Fortran INTEGER*4 兼容)或 void 值的函数从 C 中调用。

数据兼容性

  • 可以参考前一篇文档的数据类型对应关系
  • 不能按值传递数组、字符串或结构
  • 在Fortran中字符串数组长度固定,没有结束符,在C中,字符串长度可变,同时需要有一个结束符’\0’
  • 大小写问题:
    • Fortran大小写不敏感,可以任意使用
    • C对大小写敏感,因此建议在C程序中,函数名全为小写

例程名中的下划线

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

  • 在 C 函数中,通过在函数名末尾追加下划线来更改该名称。
  • 使用 BIND(C) 属性声明来指明外部函数是 C 语言函数。
  • 使用 f95 -ext_names 选项编译对无下划线的外部名称的引用。

上面的方法只能3选1,一般采用的是第一种,第三种使用不广泛,第二种可以如下示例使用。

1
2
3
FUNCTION ABC
EXTERNAL XYZ
BIND(C) ABC, XYZ

BIND(C) 声明可从 Fortran 调用的 C 外部函数,以及可从 C 中作为参数调用的 Fortran 例程。Fortran 编译器在处理外部名称时通常不追加下划线。BIND(C) 必须出现在每个包含这样的引用的子程序中。在此处,用户不仅指定 XYZ 是外部 C 函数,而且还指定 Fortran 调用程序 ABC 应该可以从 C 函数调用。如果使用 BIND(C),C 函数不需要在函数名末尾追加下划线。

数组索引与顺序

Fortran 与 C 的数组索引和顺序不同。

数组索引

C 数组总是从 0 开始,而 Fortran 数组在缺省情况下是从 1 开始。有两种常用的索引处理方法:

  • 可以使用 Fortran 缺省设置。
  • 可以指定 Fortran 数组 B 以 B(0) 开始,如INTEGER B(0:2)

数组顺序

  • Fortran 数组按列主顺序存储:A(3,2)

    1
    A(1,1) A(2,1) A(3,1) A(1,2) A(2,2) A(3,2)
  • C 数组按行主顺序存储:A[3][2]

    1
    A[0][0] A[0][1] A[1][0] A[1][1] A[2][0] A[2][1]

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

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

参数传递

按引用传递参数

  1. 简单数据类型(非COMPLEX或CHARATER串)

    • C调用Fortran,实参使用指针(&a),形参不需要特殊说明,默认为引用
    • Fortran调用C,实参不需要特殊说明,默认为引用,形参使用指针(*a)
  2. COMPLEX

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

    58c8fa32a3ec4.png

  3. 字符串

    由于没有标准接口,因此不推荐在 C 与 Fortran 例程间传递字符串。

  4. 一维数组

    • C调用Fortran,传递地址
    • Fortran调用C,传递引用(默认不需更改)
  5. 二维数组

    注意行列转换,实质就是一个是(i, j ,k),另外一个是(k, j, i)

    58c8fb7da12a0.png

  6. 结构体

    • 只要相应的元素是兼容的,便可以将 C 和 Fortran 95 派生类型传递给彼此的例程。f95 接受传统的 STRUCTURE 语句
    • Fortran 95 标准要求派生类型定义中有 SEQUENCE 语句,以确保编译器保持存储序列的顺序。
  7. 指针(传递指针就会在C中形成指针的指针)

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

      58c8fd3b0b7a9.png

    • C 指针与 Fortran 95 标量指针兼容,但与数组指针不兼容。

      58c8fd7e6b2b6.png

      Cray 与 Fortran 95 指针间的主要区别是 Cray 指针的目标始终是已命名的。在许多上下文中,声明 Fortran 95 指针会自动标识其目标。另外,被调用 C 例程还需要显式 INTERFACE 块。

按值传递参数

  1. 从 C 中调用时,Fortran 95 程序应在伪参数中使用 VALUE 属性,并且应为从 Fortran 95 中调用的 C 例程提供一个 INTERFACE

    在 C 与 Fortran 95 之间传递简单数据元素

    58c9006e4c955.png

  2. 对于传统 Fortran 77,按值调用仅对简单数据可用,并且只能为调用 C 例程的 Fortran 77 例程所用。无法做到让 C 例程调用 Fortran 77 例程并按值传递参数。数组、字符串或结构最好是按引用传递。

    要将值从 Fortran 77 例程传递到 C 例程,请使用非标准 Fortran 函数 %VAL(arg) 作为调用中的一个参数。

    58c9014ee6d97.png

返回值类型

返回简单数据类型

直接返回

返回COMPLEX数据

具体参见Here,11.5.2部分

返回CHARACTER串

不建议传递字符串,具体参见Here,11.5.3部分

参考: