当C代码调用Fortran子程序时,子程序顶部会出现分段错误



我在文件test-Q.cpp中有C++代码,它调用文件getqpf.F中的Fortran子程序。在文件test-Q.cpp中,我已经将Fortran代码声明为外部代码,并使用getqpf_()名称篡改约定调用该函数。gccgfortran编译器正在GNU/Linux上使用。

以下是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代码函数。在GNU/Linux上,使用gfortrangcc编译器,我以以下方式编译并链接了所有文件:

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行出现了segfault。它位于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

这里发生了什么,为什么会出现segfault?内存似乎没有在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

虽然支持M.S.B.关于使用F2003的C互操作性的建议,但请注意,您的具体问题是通过引用/通过值不匹配(即使在使用C互操作时,这仍然是您必须考虑的问题)。典型的Fortran实现通过引用传递所有参数,而在C(++)中,默认值是通过值传递的。在C++方面,请注意,所有int和float参数以及一些双参数都缺少指针说明符(*)。这些参数是按值传递的,但Fortran方面没有任何内容表明这一点。在F2003之前,这通常是使用Fortran代码中编译器特定的指令来完成的。

使用F2003的C互操作,具有BIND(C)属性的过程的参数的默认传递约定是通过引用。按值传递的参数需要在其声明中具有value属性。

我建议使用Fortran ISO C绑定。这里有Stackoverflow和gfortran手册中的示例。它是Fortran 2003语言标准的一部分,在此之前是Fortran 95的技术报告。这使得它具有编译器和平台可移植性。您不必担心编译器特定的调用约定或名称篡改。

相关内容

  • 没有找到相关文章

最新更新