Fortran函数调用中地址未正确传递

3

我一直在尝试通过在C ++中声明ARPACK函数(使用外部“C”)来找到矩阵的特征向量:

extern "C" {void znaupd_(int *IDO, char *BMAT, int *N, char *WHICH,
                 int *NEV, double *TOL, complex<double> *RESID,
             int *NCV, complex<double> *V, int *LDV,
             int *IPARAM, int *IPNTR, complex<double> *WORKD,
             complex<double> *WORKL, int *LWORKL, 
             double *RWORK, int *INFO);}

extern "C" {void zneupd_(bool *RVEC, char *HOWMNY, bool *SELECT, 
             complex<double> *D, complex<double> *Z,
             int *LDZ, complex<double> *WORKEV, 
             complex<double> *SIGMA, char *BMAT, int *N,
             char *WHICH, int *NEV, double *TOL, 
             complex<double> *RESID, int *NCV, 
             complex<double> *V, int *LDV, int *IPARAM,
             int *IPNTR, complex<double> *WORKD, 
             complex<double> *WORKL, int *LWORKL, int *INFO);}

然后,在我的代码主体中,我调用这些函数:
  do{
    znaupd_(&IDO, &BMAT, &N, WHICH, &NEV, &TOL, RESID, &NCV, V, &LDV, IPARAM,
        IPNTR, WORKD, WORKL, &LWORKL, RWORK, &INFO);
    switch(abs(IDO)){
    case - 1:
      for(i = 0; i < N; i++) X[i] = WORKD[IPNTR[1] + i];
      gmm::mult(SM, X, Y);
      for(i = 0; i < N; i++) WORKD[IPNTR[2] + i] = Y[i];
      break;
    case 1:
      for(i = 0; i < N; i++) X[i] = WORKD[IPNTR[1] + i]; 
      gmm::mult(SM, X, Y);
      for(i = 0; i < N; i++) 
    {WORKD[IPNTR[2] + i] = Y[i];
      WORKD[IPNTR[3] + i] = X[i];}
      break;
    case 2:
      for(i = 0; i < N; i++)
    WORKD[IPNTR[2] + i] = WORKD[IPNTR[1] + i];
      break;
    }
  }while(IDO != 99);
  std::cout << &INFO << std::endl;
  zneupd_(&RVEC, &HOWMNY, SELECT, D, Z, &LDZ, WORKEV, &SIGMA, &BMAT, &N,
      WHICH, &NEV, &TOL, RESID, &NCV, V, &LDV, IPARAM, IPNTR, WORKD,
      WORKL, &LWORKL, &INFO);

在编译和执行后,程序会出现段错误。通过使用GDB进行回溯,可以发现由&INFO传递给zneupd_的地址为0x0,导致zneupd_在尝试将新值分配到该位置时发生段错误。然而,当我上移到下一个帧并使用print &INFO时,被告知INFO存储在寄存器0x28a27c中。由于某种原因,我的程序未能正确地将INFO的位置传递给zneupd_。更令人困惑的是,znaupd_能够正确接收&INFO,并且能够访问和修改该位置的值,没有任何问题。有谁能告诉我为什么一个函数可以正确接收参数,而另一个函数不能?


对于某些编译器,您必须指定函数的导出签名。 (http://www.yolinux.com/TUTORIALS/LinuxTutorialMixingFortranAndC.html) - Mario
1
漂亮的缩进和大括号风格! - user1233963
@user1233963,点赞表示讽刺讽刺自己 :-) - user1220978
1个回答

3
您可能正在阅读错误的ARPACK文档,其中ZNEUPD子程序的描述有误。实际上,在INFO之前还有一个附加参数RWORK(总共有24个参数),并且SIGMA出现在WORKEV之前-请参见官方文档和一些源代码。在您的情况下,&INFO0只是一个幸运的巧合。

然而,你的代码中还有另一个问题 - 在x86/x64架构上,大多数Fortran编译器都会出现子程序的C原型不正确的情况。这是由于CHARACTER参数BMATWHICHHOWMNY是字符数组(字符串),每个实际字符串参数的长度也作为附加的隐藏整数类型参数通过值传递。因此,ZNAUPD的原型应该是:

extern "C"
void znaupd_(int *IDO,
             char *BMAT,
             int *N,
             char *WHICH,
             int *NEV,
             double *TOL,
             complex<double> *RESID,
             int *NCV,
             complex<double> *V,
             int *LDV,
             int *IPARAM,
             int *IPNTR,
             complex<double> *WORKD,
             complex<double> *WORKL,
             int *LWORKL, 
             double *RWORK,
             int *INFO,
             int _BMAT,    // The length of the actual BMAT argument
             int _WHICH    // The length of the actual WHICH argument
            );

同样适用于ZNEUPD,但它有三个隐藏的整数参数:
extern "C"
void zneupd_(bool *RVEC,
             char *HOWMNY,
             bool *SELECT, 
             complex<double> *D,
             complex<double> *Z,
             int *LDZ,
             complex<double> *SIGMA,
             complex<double> *WORKEV, 
             char *BMAT,
             int *N,
             char *WHICH,
             int *NEV,
             double *TOL, 
             complex<double> *RESID,
             int *NCV, 
             complex<double> *V,
             int *LDV,
             int *IPARAM,
             int *IPNTR,
             complex<double> *WORKD, 
             complex<double> *WORKL,
             int *LWORKL,
             double *RWORK,
             int *INFO,
             int _HOWMNY,
             int _BMAT,
             int _WHICH
            );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接