fortran与c语言接口参数传递混合编程.docx
《fortran与c语言接口参数传递混合编程.docx》由会员分享,可在线阅读,更多相关《fortran与c语言接口参数传递混合编程.docx(29页珍藏版)》请在冰豆网上搜索。
fortran与c语言接口参数传递混合编程
SunStudio12:
Fortran编程指南
∙Previous:
第10章并行化
第11章C-Fortran接口
本章论述Fortran与C的互操作性方面的问题,内容仅适用于SunStudioFortran95和C编译器的特定情况。
11.9Fortran2003与C的互操作性简要说明了Fortran2003标准第15部分中提到的C绑定功能。
(此标准可以从国际Fortran标准Web站点http:
//www.j3-fortran.org 获得)。
Fortran95编译器实现了标准中所述的这些功能。
如不特别注明,32位x86处理器视为与32位SPARC处理器等同。
对于64位x86处理器和64位SPARC处理器也是如此,只是x86系统未定义REAL*16和COMPLEX*32数据类型,这些数据类型只能用于SPARC。
11.1兼容性问题
大多数 C-Fortran接口必须在以下这些方面全部保持一致:
∙函数和子例程的定义及调用
∙数据类型的兼容性
∙参数传递(按引用或按值)
∙参数的顺序
∙过程名(大写、小写或带有结尾下划线(_))
∙向链接程序传递正确的库引用
某些C-Fortran接口还必须符合:
∙数组索引及顺序
∙文件描述符和 stdio
∙文件权限
11.1.1函数还是子例程?
函数一词在 C和Fortran中有不同的含义。
根据具体情况做出选择很重要:
∙在C中,所有的子程序都是函数;但 void 函数不会返回值。
∙在Fortran中,函数会传递一个返回值,但子例程一般不传递返回值。
当Fortran例程调用C函数时:
∙如果被调用的C函数返回一个值,则将其作为函数从Fortran中调用。
∙如果被调用的C函数不返回值,则将其作为子例程调用。
当C函数调用Fortran子程序时:
∙如果被调用的Fortran子程序是一个函数,则将其作为一个返回兼容数据类型的函数从C中调用。
∙如果被调用的Fortran子程序是一个子例程,则将其作为一个返回 int(与Fortran INTEGER*4 兼容)或 void 值的函数从C中调用。
如果Fortran子例程使用交替返回,则会返回一个值,这种情况下它是 RETURN 语句中的表达式的值。
如果 RETURN 语句中没有出现表达式,但在 SUBROUTINE 语句中声明了交替返回,则会返回零。
11.1.2数据类型的兼容性
表11–2 总结了Fortran95(与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),按值将参数从Fortran95例程传递到C例程。
假如Fortran例程具有一个显式接口块,该接口块用VALUE 属性声明了伪参数,则可以按值将参数从C传递到Fortran95。
∙数值序列类型的组件的对齐方式与通用块的对齐方式相同,也会受到 -aligncommon 选项的影响。
数值序列类型是这样一种序列类型:
其中所有组件的类型为缺省整数、缺省实数、双精度实数、缺省复数或缺省逻辑,而不是指针。
∙在大多数情况下,非数值序列类型的数据类型组件以自然对齐的方式对齐,但QUAD变量除外。
对于四精度变量,32位SPARC平台和64位SPARC平台之间的对齐方式不同。
∙在所有平台上,用BIND(C)属性定义的VAX结构和数据类型的组件始终与C结构具有相同的对齐方式。
表11–1数据大小与对齐-(以字节表示)按引用传递(f95 和 cc)
Fortran95数据类型
C数据类型
大小
对齐
BYTEx
charx
1
1
CHARACTERx
unsignedcharx;
1
1
CHARACTER(LEN=n)x
unsignedcharx[n];
n
1
COMPLEXx
struct{floatr,i;}x;
8
4
COMPLEX(KIND=4)x
COMPLEX(KIND=8)x
COMPLEX(KIND=16)x (SPARC)
struct{floatr,i;}x;
struct{doubledr,di;}x;
struct{longdouble,dr,di;}x;
8
16
32
4
4/8
4/8/16
DOUBLECOMPLEXx
struct{doubledr,di;}x;
16
4/8
DOUBLEPRECISIONx
doublex;
8
4
REALx
floatx;
4
4
REAL(KIND=4)x
REAL(KIND=8)x
REAL(KIND=16)x (SPARC)
floatx;
doublex;
longdoublex;
4
8
16
4
4/8
4/8/16
INTEGERx
intx;
4
4
INTEGER(KIND=1)x
INTEGER(KIND=2)x
INTEGER(KIND=4)x
INTEGER(KIND=8)x
signedcharx;
shortx;
intx;
longlongintx;
1
2
4
8
4
4
4
4
LOGICALx
intx;
4
4
LOGICAL(KIND=1)x
LOGICAL(KIND=2)x
LOGICAL(KIND=4)x
LOGICAL(KIND=8)x
signedcharx;
shortx;
intx;
longlongintx;
1
2
4
8
4
4
4
4
11.1.3大小写敏感性
C和Fortran在区分大小写方面采取截然相反的处理方法:
∙C区分大小写-大小写很重要。
∙Fortran在缺省情况下忽略大小写。
f95 缺省通过将子程序名转换成小写来忽略大小写。
除了字符串常量以外,它会将所有大写字母都转换成小写字母。
对于大/小写问题,有两种常用解决方案:
∙在C子程序中,使C函数名全为小写。
∙用 -U 选项编译Fortran程序,该选项会通知编译器保留函数/子程序名称的现有大/小写区别。
只能采用这两种解决方案中的一种,不能同时采用。
本章大多数示例的C函数名均采用小写字母,并且没有使用 f95-U 编译器选项。
11.1.4例程名中的下划线
Fortran编译器通常会在入口点定义和调用中都出现的子程序名末尾追加一个下划线(_)。
该惯例不同于具有相同的用户指定名称的C过程或外部变量。
几乎所有Fortran库过程名都有两个前导下划线,以减少与用户指定的子例程名的冲突。
对于下划线问题,有三种常用解决方案:
∙在C函数中,通过在函数名末尾追加下划线来更改该名称。
∙使用 BIND(C) 属性声明来指明外部函数是C语言函数。
∙使用 f95 -ext_names 选项编译对无下划线的外部名称的引用。
只能使用上述解决方案中的一种。
本章的示例都可以使用 BIND(C) 属性声明来避免下划线。
BIND(C) 声明可从Fortran调用的C外部函数,以及可从C中作为参数调用的Fortran例程。
Fortran编译器在处理外部名称时通常不追加下划线。
BIND(C) 必须出现在每个包含这样的引用的子程序中。
惯常用法是:
FUNCTIONABC
EXTERNALXYZ
BIND(C)ABC,XYZ
在此处,用户不仅指定 XYZ 是外部C函数,而且还指定Fortran调用程序 ABC 应该可以从C函数调用。
如果使用 BIND(C),C函数不需要在函数名末尾追加下划线。
11.1.5按引用或值传递参数
通常,Fortran例程按引用传递参数。
在调用中,如果非标准函数 %VAL() 中包含一个参数,则调用例程会按值传递该参数。
Fortran95按值传递参数的标准方法是通过VALUE属性和INTERFACE块。
请参见 11.4按值传递数据参数。
C通常按值传递参数。
如果在参数前加上表示“和”的符号(&),C会使用指针按引用传递参数。
C总是按引用传递数组和字符串。
11.1.6参数顺序
除字符串参数之外,Fortran和C均以相同的顺序传递参数。
但对于每个字符型参数,Fortran例程都会传递一个附加参数,用以指定串长度。
这些参数在C中是 long int 数量,按值进行传递。
参数顺序为:
∙与每个参数相应的地址(数据或函数)
∙与每个字符参数对应的 long int(字符串长度的完整列表位于其他参数的完整列表之后)
示例:
Fortran代码片段:
等价的C代码片段:
CHARACTER*7S
INTEGERB(3)
...
CALLSAM(S,B
(2))
chars[7];
intb[3];
...
sam_(s,&b[1],7L);
11.1.7数组索引和顺序
Fortran与C的数组索引和顺序不同。
11.1.7.1数组索引
C 数组总是从0开始,而Fortran数组在缺省情况下是从1开始。
有两种常用的索引处理方法。
∙如上述示例所示,可以使用Fortran缺省设置。
此时,Fortran元素 B
(2) 等同于C元素 b[1]。
∙可以指定Fortran数组B以B(0)开始,如下所示:
INTEGERB(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
FortranI/O通道采用的是单元号。
底层SunOS操作系统不处理单元号,而是处理文件描述符。
Fortran运行时系统会不断变换,所以大多数Fortran程序没必要识别文件描述符。
许多C程序都使用一组称为标准I/O(即 stdio)的子例程。
有许多FortranI/O函数也使用标准I/O,而后者又使用操作系统I/O调用。
下表列出了这些I/O系统的某些特性。
表11–2Fortran与C之间的I/O比较
Fortran单元
标准I/O文件指针
文件描述符
文件打开
为读写打开
为读打开、为写打开、为读写打开,或者为追加打开;请参见 open
(2)
为读打开、为写打开或同时为读写打开
属性
已格式化或未格式化
始终未格式化,但可用格式解释例程进行读或写
始终未格式化
访问
直接或顺序
直接访问(如果物理文件的表示是直接访问),但总是可以按顺序读取
直接访问(如果物理文件的表示是直接访问),但总是可以按顺序读取
结构
记录
字节流
字节流
形式
0-2147483647间的任意非负整数
指向用户地址空间中结构的指针
0-1023间的整数
11.1.9库与使用 f95 命令链接
要链接正确的Fortran和C 库,请使用 f95 命令调用链接程序。
示例1:
用编译器进行链接:
demo%cc-csomeCroutine.c
demo%f95theF95routine.fsomeCroutine.o<-链接步骤
demo%a.out
4.04.5
8.09.0
demo%
11.2Fortran初始化例程
用 f95 编译的主程序在程序启动时会调用库中的伪初始化例程 f90_init。
库中的这些例程是不进行任何操作的伪例程。
编译器生成的调用将指针传递到程序的参数和环境。
这些调用会提供软件挂钩,您可以在C中用软件挂钩提供自己的例程,以便在程序启动之前以任何定制方式初始化程序。
这些初始化例程的一种可能用途是,为国际化Fortran程序调用 setlocale。
由于 setlocale 在 libc 以静态方式链接时不起作用,因此只有以动态方式链接了 libc 的Fortran程序才能进行国际化。
库中 init 例程的源代码如下
voidf90_init(int*argc_ptr,char***argv_ptr,Char***envp_ptr){}
f90_init 由 f95 主程序调用。
参数分别被设置为 argc、argv 和 envp 的地址。
11.3按引用传递数据参数
在Fortran例程和C过程之间传递数据的标准方法是按引用传递。
对于C过程而言,Fortran子例程或函数调用就像是一个所有参数均用指针表示的过程调用。
唯一特殊的是Fortran将字符串和函数作为参数及CHARACTER*n 函数的返回值进行处理的方式。
11.3.1简单数据类型
对于简单数据类型(非COMPLEX或CHARACTER串),将C例程中的每个关联参数作为指针定义或传递:
表11–3传递简单数据类型
Fortran调用C
C调用Fortran
integeri
realr
externalCSim
i=100
callCSim(i,r)
...
----------------------------
voidcsim_(int*i,float*r)
{
*r=*i;
}
inti=100;
floatr;
externvoidfsim_(int*i,float*r);
fsim_(&i,&r);
...
------------------------------
subroutineFSim(i,r)
integeri
realr
r=i
return
end
11.3.2COMPLEX数据
将FortranCOMPLEX数据项作为指针传递到具有两种浮点或两种双精度数据类型的C结构:
表11–4传递COMPLEX数据类型
Fortran调用C
C调用Fortran
complexw
doublecomplexz
externalCCmplx
callCCmplx(w,z)
...
------------------------------
structcpx{floatr,i;};
structdpx{doubler,i;};
voidccmplx_(
structcpx*w,
structdpx*z)
{
w->r=32.;
w->i=.007;
z->r=66.67;
z->i=94.1;
}
structcpx{floatr,i;};
structcpxd1;
structcpx*w=&d1
structdpx{doubler,i;};
structdpxd2;
structdpx*z=&d2
fcmplx_(w,z);
...
---------------
subroutineFCmplx(w,z)
complexw
doublecomplexz
w=(32.,.007)
z=(66.67,94.1)
return
end
在64位环境下,在寄存器中返回 COMPLEX 值。
11.3.3字符串
由于没有标准接口,因此不推荐在C与Fortran例程间传递字符串。
不过,请注意以下方面:
∙所有C字符串均按引用传递。
∙Fortran调用会为参数列表中具有字符类型的每个参数传递一个附加参数。
此额外参数给出串长度,它等同于按值传递的C长整数。
(这要依具体实现而定。
)额外的串长度参数出现在调用中的显式参数之后。
下例展示了具有字符串参数的Fortran调用及其等同的C调用:
表11–5传递CHARACTER串
Fortran调用:
等价的C调用:
CHARACTER*7S
INTEGERB(3)
...
CALLCSTRNG(S,B
(2))
...
chars[7];
intb[3];
...
cstrng_(s,&b[1],7L);
...
如果在被调用例程中不需要串长度,则可以忽略额外的参数。
但要注意,Fortran不会自动以C期望的显式空字符来终结字符串。
该终结符必须由调用程序添加。
字符数组调用与单个字符变量调用看起来一样。
会传递数组的起始地址,所使用的长度是数组中单个元素的长度。
11.3.4一维数组
在C中数组下标以0开始。
表11–6传递一维数组
Fortran调用C
C调用Fortran
integeri,Sum
integera(9)
externalFixVec
...
callFixVec(a,Sum)
...
------------------------------
voidfixvec_(
intv[9],int*sum)
{
inti;
*sum=0;
for(i=0;i<=8;i++)
*sum=*sum+v[i];
}
externvoidvecref_
(int[],int*);
...
inti,sum;
intv[9]=...
vecref_(v,&sum);
...
------------------------------
subroutineVecRef(v,total)
integeri,total,v(9)
total=0
doi=1,9
total=total+v(i)
enddo
...
11.3.5二维数组
C与Fortran间的行列转换。
表11–7传递二维数组
Fortran调用C
C调用Fortran
REALQ(10,20)
...
Q(3,5)=1.0
CALLFIXQ(Q)
...
------------------------------
voidfixq_(floata[20][10])
{
...
a[5][3]=a[5][3]+1.;
...
}
externvoid
qref_(int[][10],int*);
...
intm[20][10]=...;
intsum;
...
qref_(m,&sum);
...
------------------------------
SUBROUTINEQREF(A,TOTAL)
INTEGERA(10,20),TOTAL
DOI=1,10
DOJ=1,20
TOTAL=TOTAL+A(I,J)
ENDDO
ENDDO
...
11.3.6结构
只要相应的元素是兼容的,便可以将C和Fortran95派生类型传递给彼此的例程。
(f95 接受传统的 STRUCTURE 语句。
)
表11–8传递传统FORTRAN77STRUCTURE记录
Fortran调用C
C调用Fortran
STRUCTURE/POINT/
REALX,Y,Z
ENDSTRUCTURE
RECORD/POINT/BASE
EXTERNALFLIP
...
CALLFLIP(BASE)
...
------------------------------
structpoint{
floatx,y,z;
};
voidflip_(structpoint*v)
{
floatt;
t=v->x;
v->x=v->y;
v->y=t;
v->z=-2.*(v->z);
}
structpoint{
floatx,y,z;
};
voidfflip_(structpoint*);
...
structpointd;
structpoint*ptx=&d;
...
fflip_(ptx);
...
------------------------------
SUBRO