7.1 ISO C绑定:与C语言的无缝对接


7.1 ISO C绑定:与C语言的无缝对接

本节摘要:Fortran 与 C 互相调用,难点在两边默认的"规矩"不同:传参默认传址对传值、数组列优先对行优先、符号名带下划线对原样导出。ISO C 绑定(iso_c_binding 模块 + bind 子句)把这些差异全部显式化。本节讲清类型映射、bind 名称、双向调用的写法,让你在两门语言之间搭起一条类型安全的桥。

两门语言第一次见面

Fortran 调 C 库(比如 BLAS、MPI 的 C 接口、系统 API),或把 Fortran 内核暴露给 C 外壳——历史上这靠编译器特定的"魔法",换个编译器就断。ISO C 绑定(Fortran 2003)把这件事标准化了:类型怎么对应、符号名怎么导出、参数怎么传,全部写清楚,让编译器不再猜。它不消灭两门语言的差异,而是把差异变成显式约定。

bind(c):给编译器一张互操作地图

核心是 use iso_c_binding 导入类型常量,配合 bind(c) 声明。类型映射表要背熟:c_int 对应整数、c_double 对应双精度、c_float 对应单精度、c_ptr 对应 C 指针、c_char 对应字符。bind(c) 告诉编译器"这个实体按 C 的调用约定导出,符号名就用 bind 里给的名字"。

module interop_mod use iso_c_binding, only: c_int, c_double implicit none ! Fortran 侧声明一个与 C 对齐的接口 interface function c_sqrt(x) bind(c, name="sqrt") result(r) import :: c_double real(c_double), value :: x ! C 按值传参 real(c_double) :: r end function c_sqrt end interface contains ! Fortran 侧导出给 C 调用的子程序 subroutine compute_sum(n, x, s) bind(c, name="compute_sum") integer(c_int), value :: n real(c_double), dimension(n) :: x real(c_double), intent(out) :: s s = sum(x) end subroutine compute_sum end module interop_mod

name="sqrt" 显式指定 C 侧符号名,绕开 Fortran 默认的名称修饰(带下划线等)。value 属性对应 C 的按值传参——C 函数参数默认按值,Fortran 默认传址,在 interface 块里用 value 对齐。dimension(n) 的 x 对应 C 侧一个数组指针,n 是长度,这是 C 里常见的"指针加长度"约定。

从 Fortran 调 C

上面 interface 块里的 c_sqrt 就是调用 C 标准库 sqrt 的桥。调用的代码和普通函数一样:

program call_c use interop_mod, only: c_sqrt use iso_c_binding, only: c_double implicit none real(c_double) :: v v = c_sqrt(2.0_c_double) write(*,*) 'sqrt(2) =', v end program call_c

要点是 interface 块必须和 C 函数签名严格一致——类型、个数、传值传址都对上。对不上就是运行时内存错乱,Fortran 帮不了你。数组传过去要记住:C 侧看到的是首地址,长度靠 n 参数约定,Fortran 的边界检查救不了跨语言调用。

从 C 调 Fortran

反向也一样:Fortran 子程序带 bind(c, name) 导出,C 侧按名字声明 extern 再调用。关键是数组布局:Fortran 列优先,C 行优先,二维数组跨语言必须同时转置索引或用一维接口,否则数据错位。

/* C 侧声明并调用 Fortran 导出的 compute_sum */ extern void compute_sum(int n, const double *x, double *s); int main(void) { double x[3] = {1.0, 2.0, 3.0}; double s = 0.0; compute_sum(3, x, &s); printf("sum = %f\n", s); return 0; }

图:Fortran 与 C 的互操作边界

图:Fortran 与 C 的互操作边界

边界的规矩

互操作代码要遵循几条纪律。第一,interface 块签名与 C 函数必须逐项对齐,宁可多写 import 也不留默认猜测。第二,二维以上数组跨语言统一用一维接口加显式维度,或在 C 侧处理转置,别指望两边布局自动一致。第三,字符与字符串的跨语言传递最坑,C 以空字符结尾、Fortran 定长,要么用 c_char 数组手管,要么避免在热路径上传字符串。第四,跨语言调用不做边界检查,所有错误都是未定义行为,把检查放在 Fortran 入口内部。这些规矩的共性:把"编译器默认"全部替换成"显式约定"。

学习目标

阅读完本节,你应当能够:

  1. 说出 iso_c_binding 提供的常用类型常量与 bind 子句的作用
  2. 写 interface 块从 Fortran 调用一个 C 函数
  3. 写 bind(c) 的子程序并说明如何从 C 侧调用它
  4. 解释名称修饰与传值传址两个边界差异的处理

本节要点回顾

  • iso_c_binding 提供类型映射:c_int、c_double、c_ptr、c_char 对应 C 侧类型
  • bind(c, name) 消除名称修饰:符号名显式指定,两边不再猜
  • value 对齐 C 的按值传参:interface 块里逐参数声明传值传址
  • 数组跨语言是首地址加长度:二维以上用一维接口,谨记列优先与行优先差异
  • 互操作代码零检查:签名错位是未定义行为,检查留在 Fortran 入口内部
  • 显式约定代替编译器默认:这是整个互操作层的设计主线

标量与数组能通了,看更复杂的结构怎么传——7.2 节。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U