【问题标题】:Segmentation fault occurs at top of subroutine when C code calls Fortran subroutineC 代码调用 Fortran 子程序时,子程序顶部出现分段错误
【发布时间】:2012-11-16 17:22:36
【问题描述】:

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

这是 C++ 文件顶部的 sn-p:

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 文件的相应 sn-p:

   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 代码函数。在使用 gfortrangcc 编译器的 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

【问题讨论】:

    标签: c++ gcc segmentation-fault fortran gfortran


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢,伊恩!我认为这可能是事情出错的地方。我需要在我的代码中进行哪些更改以确保不会发生崩溃?当我在 extern "C" 大括号中声明函数时,所有参数都必须是指针吗?那么如何调用函数呢?
    【解决方案2】:

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

    【讨论】:

    • 谢谢,M.S.B.绑定是否也解决了内存传递问题,或者这只是一种确保以正确方式调用子例程的方法?
    • 我也在寻找一个相当简洁的例子,以及如何使用 Fortran ISO 绑定的重点示例。我该如何修改上面的代码?我不知道我是否设法找到了一个类似于我上面发布的代码 sn-p 示例。
    • 如果在 Fortran 中使用 ISO C 绑定对 C 例程的正确描述,它会导致 Fortran 编译器使用与 C 编译器兼容的调用约定。两者一起工作。请参阅 gfortran 手册的“混合语言编程”和“内部模块”章节的 ISO C 绑定。
    • 这是一个stackoverflow示例:stackoverflow.com/questions/8207997/…
    • 谢谢,M.S.B.该示例显示了如何为字符(传递的字符串)设置 ISO 绑定,但我如何将其用于其他数据类型(例如我的代码中的浮点数和双精度数)?
    猜你喜欢
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    相关资源
    最近更新 更多