Адрес неправильно передан вызову Фортрана

Я пытался найти собственные векторы матрицы, объявив функции ARPACK в C++, используя extern "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 показывает, что адрес, передаваемый zneupd_ с помощью &INFO, равен 0x0, что вызывает ошибку segfault, когда zneupd_ пытается присвоить новое значение этой позиции. Однако, когда я перехожу к следующему кадру и использую print &INFO, мне сообщают, что INFO хранится в регистре 0x28a27c. По какой-то причине моя программа неправильно передает информацию о местоположении в zneupd_. Что еще более озадачивает, так это то, что znaupd_ способен правильно получать &INFO и может без проблем получать доступ и изменять значение в этом месте. Кто-нибудь может сказать мне, почему одна функция может получить аргумент правильно, а другая - нет?

1 ответ

Решение

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

Однако в вашем коде есть еще одна проблема - прототипы подпрограмм на C были бы некорректны с большинством компиляторов Fortran на x86/x64. Это связано с тем, что CHARACTER аргументы BMAT, 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
            );
Другие вопросы по тегам