问题
I have C++ code in file test-Q.cpp
that calls a Fortran subroutine in file getqpf.F
. In file test-Q.cpp
, I've declared the Fortran code as external, and I am calling the function using the getqpf_()
name-mangling convention. The gcc
and gfortran
compilers are being used on GNU/Linux.
Here is a snippet from the top of the C++ file:
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
Here is a corresponding snippet from the Fortran file:
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
The Fortran code calls other C-code functions. On GNU/Linux using the gfortran
and gcc
compilers I've compiled and linked all of the files in the following manner:
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
Although I am able to build the binary successfully, there is a segfault that occurs at the line nf = nfreqfit
. This is situated at the very top of the Fortran file. Running gdb
on the binary produces the following output:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000406fd3 in getqpf (tri=..., nsamp=Cannot access memory at address 0x3e9
) at getqpf.F:44
44 nf = nfreqfit
What is happening here, and why is there a segfault? It appears that memory is not being properly passed between the C++ code and the Fortran code.
UPDATE
As IanH mentions in the answer below, the problem is due to not passing arguments by reference. Using C++, the function must be declared as:
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
Note the presence of the ampersands. Then, the function can be called in the code as:
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);
Note that variables such as nsamp
are declared as int nsamp = 1001
.
回答1:
While seconding M.S.B.'s recommendation about using F2003's C interoperability, note that your specific issue is a pass by reference/pass by value mismatch (which is still something that you have to consider even when using C interoperability). Typical Fortran implementations pass all arguments by reference, while in C(++) the default is by value. On the C++ side, note that all of the int and float arguments and some of the double arguments lack the pointer specifier (*
). These arguments are passed by value - but there is nothing on the Fortran side to indicate that. Before F2003 this was usually done using compiler specific directives in the Fortran code.
Using F2003's C interop, the default passing convention for arguments to procedures with the BIND(C) attribute is by reference. Arguments that are passed by value need to have the VALUE attribute in their declaration.
回答2:
I recommend using the Fortran ISO C Binding. There are examples here on Stackoverflow and in the gfortran manual. It is part of the Fortran 2003 language standard and before that a Technical Report for Fortran 95. That makes it compiler and platform portable. You don't have to worry about compiler specific calling conventions or name mangling.
来源:https://stackoverflow.com/questions/13421406/segmentation-fault-occurs-at-top-of-subroutine-when-c-code-calls-fortran-subrout