2013-09-19 4 views
3

Я пытался найти собственные векторы матрицы, объявляя функции ARPACK в C++ с использованием Экстерн «C»:Адрес не прошло правильно вызова Fortran функции

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); 

После компиляции и выполнения программы segfaults. Выполнение backtrace с GDB показывает, что адрес, передаваемый zneupd_ на & INFO, равен 0x0, что вызывает segfault, когда zneupd_ пытается присвоить новое значение этой позиции. Когда я перехожу к следующему кадру и использую печать & INFO, мне сообщили, что INFO хранится в регистре 0x28a27c. По какой-то причине моя программа не передает правильное расположение INFO в zneupd_. Что еще более озадачивает, так это то, что znaupd_ может правильно получать & INFO и может без проблем получать и изменять значение в этом месте. Может ли кто-нибудь сказать мне, почему одна функция может получить аргумент правильно, а другая не может?

+0

Для некоторого компилятора вы должны указать экспортированную сигнатуру функции. (http://www.yolinux.com/TUTORIALS/LinuxTutorialMixingFortranAndC.html) – Mario

+1

Красивые отступы и стиль подставки! – user1233963

+0

@ user1233963, большой палец вверх для иронии :-) –

ответ

3

Возможно, вы читаете плохую документацию ARPACK, которая неверно описывает подпрограмму ZNEUPD. На самом деле есть дополнительный аргумент RWORK до INFO (всего 24 аргумента), а также SIGMA - до WORKEV - см. official documentation и некоторые source code. То, что &INFO приходит как 0 в вашем случае - просто счастливое совпадение.

В коде есть еще одна проблема: прототипы C подпрограмм будут некорректны для большинства компиляторов Fortran на x86/x64. Это связано с тем, что аргументы , WHICH и HOWMNY представляют собой массивы символов (строки), а длина каждого фактического строкового аргумента также передается по значению как дополнительный скрытый аргумент целочисленного типа. Поэтому прототип 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 
      ); 
Смежные вопросы