我在文件test-Q.cpp
中有C ++代码,它调用文件getqpf.F
中的Fortran子例程。在文件test-Q.cpp
中,我已将Fortran代码声明为外部代码,并且我使用getqpf_()
名称修改约定来调用该函数。 GNU / Linux上正在使用gcc
和gfortran
编译器。
以下是C ++文件顶部的代码段:
extern "C" {
void getqpf_ (double *tri,
int nsamp,
int lwin,
int nfreqfit,
double dt,
float null,
int L2,
double df,
double *qq,
double *pf,
double *ampls,
double *work1,
double *work2,
double *work3,
double *work4,
int mem,
int morder,
int nfs,
double *xReal,
double *xImag,
double *xAbs,
double *x1,
int cen,
int top,
int bot,
float cut,
int nfst,
int raw);
} // end
以下是Fortran文件中的相应代码段:
subroutine getqpf (tri, nsamp, lwin, nfreqfit, dt, null, L2, df,
1 qq, pf, ampls, work1, work2, work3, work4,
2 mem, morder, nfs, xReal, xImag, xAbs, x1,
3 cen,top,bot, cut,nfst,raw)
integer morder, lwin, nsamp, nfreqfit, delay, nfs
real tri(*)
real qq(*), pf(*), ampls(*)
real * 8 work1(*), work2(*), work3(*), work4(*)
real * 8 xReal(*), xImag(*), xabs(*), x1(*)
real * 8 dt8, cut8, df8
real null, cut
integer nfst
logical mem, L2, cen, top, bot, raw
integer nf
C program logic code starts here
nf = nfreqfit
delay = 0
dt8 = dt
cut8 = cut
Fortran代码调用其他C代码函数。在使用gfortran
和gcc
编译器的GNU / Linux上,我按以下方式编译和链接了所有文件:
g++ -c test-Q.cpp -I./boost/boost_1_52_0/ -g
gcc -c paul2.c -g
gcc -c paul2_L1.c -g
gcc -c paul6.c -g
gcc -c paul6_L1.c -g
gcc -c fit_slope.c -g
gfortran -c getqpf.F -g
g++ -o test-Q test-Q.o paul2.o paul2_L1.o paul6.o paul6_L1.o fit_slope.o getqpf.o -g
虽然我能够成功构建二进制文件,但在nf = nfreqfit
行发生了一个段错误。它位于Fortran文件的最顶层。在二进制文件上运行gdb
会产生以下输出:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000406fd3 in getqpf (tri=..., nsamp=Cannot access memory at address 0x3e9
) at getqpf.F:44
44 nf = nfreqfit
这里发生了什么,为什么会出现段错误?看起来内存没有在C ++代码和Fortran代码之间正确传递。
更新
正如IanH在下面的答案中提到的,问题是由于没有通过引用传递参数。使用C ++,该函数必须声明为:
extern"C" {
void getqpf_ (float *tri,
int &nsamp,
int &lwin,
int &nfreqfit,
float &dt,
float &null,
int &L2,
float &df,
float *qq,
float *pf,
float *ampls,
double *work1,
double *work2,
double *work3,
double *work4,
int &mem,
int &morder,
int &nfs,
double *xReal,
double *xImag,
double *xAbs,
double *x1,
int &cen,
int &top,
int &bot,
float &cut,
int &nfst,
int &raw);
} // end
请注意&符号的存在。然后,可以在代码中调用该函数:
getqpf_ (tri,
nsamp,
lwin,
nfreqfit,
dt,
null,
L2,
df,
qq,
pf,
ampls,
work1,
work2,
work3,
work4,
mem,
morder,
nfs,
xReal,
xImag,
xAbs,
x1,
cen,
top,
bot,
cut,
nfst,
raw);
请注意,nsamp
等变量声明为int nsamp = 1001
。
答案 0 :(得分:2)
我建议使用Fortran ISO C绑定。 Stackoverflow和gfortran手册中有一些示例。它是Fortran 2003语言标准的一部分,在此之前是Fortran 95的技术报告。这使得编译器和平台可移植。您不必担心编译器特定的调用约定或名称重整。
答案 1 :(得分:2)
虽然支持M.S.B.关于使用F2003的C互操作性的建议,但请注意,您的具体问题是通过引用传递/传递值不匹配(即使使用C互操作性,您仍需要考虑这一问题)。典型的Fortran实现通过引用传递所有参数,而在C(++)中,默认值是按值传递。在C ++方面,请注意所有int和float参数以及一些双参数都缺少指针说明符(*
)。这些参数是通过值传递的 - 但Fortran方面没有任何内容表明这一点。在F2003之前,这通常是使用Fortran代码中的编译器特定指令完成的。
使用F2003的C interop,具有BIND(C)属性的过程的参数的默认传递约定是引用。通过值传递的参数需要在其声明中具有VALUE属性。