Передача выделенного C_PTR массиву Fortran в C

У меня возникли проблемы с segfaults от доступа к массиву на C, который указан в файле Fortran ниже. Есть несколько артефактов отладки, таких как факт, что запись файла не пишет ничего значимого, и я инициализирую переменную i которую я никогда не использую.

Однако я нашел следующее:

  • Не инициализируйте i (но все еще объявляю это): no segfault
  • Не открывать файл в C: no segfault
  • Не печатать HESS (не HESS_COPY ) в другом месте в коде: no segfault
  • Объявление и инициализация i с другим именем: segfault

Кто-нибудь знает, что может привести к такому поведению? Сама segfault встречается на линии ARRAY_PTR = C_LOC(HESS_COPY(1, 1)) . Я компилирую gfortran и gcc с флагами отладки (без оптимизации).

valgrind говорит, что есть недопустимая запись (верхние два файла – те, которые показаны ниже):

  Invalid write of size 8 at 0xBEEA3E: get_pointer (modsparsehess.f90:34) by 0xA75D7A: print_hess (sparse_hessian_c.c:108) by 0x866C95: quench_ (quench.f:316) by 0x7F2DBE: mc_ (mc.F:368) by 0x4B65E2: mcruns_ (mcruns.f:62) by 0x459245: MAIN__ (main.F:430) by 0x45A33F: main (main.F:21) Address 0x87 is not stack'd, malloc'd or (recently) free'd 

C-файл

 #include  void get_pointer(double ** hessian); void print_hess(int *arr_size) { // Create a pointer to handle the hessian double **hessian; int i; i = 0; get_pointer(hessian); printf("%8.3f", **hessian); // Open a file for writing FILE *fptr = fopen("hessian_out", "w"); // Print the hessian fprintf(fptr, "\n"); fclose(fptr); } 

Файл Fortran

 MODULE MODSPARSEHESS USE, INTRINSIC :: ISO_C_BINDING USE MODHESS, ONLY: HESS INTERFACE SUBROUTINE PRINT_HESSIAN(DIMENSIONS) BIND(C, NAME='print_hess') USE, INTRINSIC :: ISO_C_BINDING INTEGER(C_INT) :: DIMENSIONS END SUBROUTINE PRINT_HESSIAN END INTERFACE CONTAINS SUBROUTINE GET_POINTER_IN_C(ARRAY_PTR) BIND(C, NAME='get_pointer') ! C signature: void get_pointer(double ** hessian); USE, INTRINSIC :: ISO_C_BINDING IMPLICIT NONE ! Arguments TYPE(C_PTR), INTENT(OUT) :: ARRAY_PTR ! Local variables REAL(C_DOUBLE), DIMENSION(:,:), & ALLOCATABLE, TARGET :: HESS_COPY ! Copy the hessian into HESS_COPY IF (.NOT. ALLOCATED(HESS_COPY)) THEN ALLOCATE(HESS_COPY(SIZE(HESS, 1), SIZE(HESS, 2))) END IF HESS_COPY(:, :) = HESS(:, :) ! Now get the pointer ARRAY_PTR = C_LOC(HESS_COPY(1, 1)) END SUBROUTINE GET_POINTER_IN_C END MODULE MODSPARSEHESS 

Переменная HESS_COPY является локальной, несохраненной, распределяемой переменной процедуры Fortran GET_POINTER_IN_C .

Следовательно, всякий раз, когда процедура начинается, она всегда нераспределяется. Поэтому проверка его статуса распределения в первом исполняемом заявлении этой процедуры является излишней.

Следовательно, также, в конце выполнения процедуры, что несохраненная локальная переменная автоматически освобождается. Таким образом, ссылка C_LOC в конце процедуры получает адрес объекта, который вот-вот прекратит существование.

Затем код C работает с адресом объекта, который не существует, и программа выходит из строя.

Если для переменной HESS_COPY была сохранена локальная или сохраненная переменная модуля, она будет существовать между HESS_COPY процедур.

(Все переменные модуля сохраняются с Fortran 2008, предыдущие языковые версии формально требовали явного специфического SAVE для соответствующих переменных модуля, если модуль не постоянно ссылался в активной области.)

(В стороне, правила языка, как и Fortran 2003, также означают, что выделенный тест, оператор HESS_COPY = HESS и последующее присваивание могут быть просто заменены одним утверждением HESS_COPY = HESS .)


Кроме того, в коде C делается попытка вернуть информацию в указатель, который не существует. В исходном коде hessian объявляется как указатель на указатель, чтобы удвоить – отметить два уровня косвенности. Без какой-либо инициализации первый уровень косвенности будет указывать «случайно» в памяти, тогда код Fortran будет пытаться сохранить его результат в этом случайном месте.

В качестве альтернативы рассмотрим:

 #include  void get_pointer(double ** hessian); void print_hess(int *arr_size) { // A pointer to double (one level of indirection). double *hessian; // Pass the address of that pointer. get_pointer(&hessian); // print the value of the double being pointed at. printf("%8.3f\n", *hessian); // print the value of the next double in the array // (assuming that there is more than one). printf("%8.3f\n", *(hessian+1)); // (or equivalently, `hessian[1]`) } 

Метод указателя Фортран, на который ссылается Владимир Ф. в комментариях, требует двух процедур Фортрана – одного из тех, которые у вас есть, который выделяет указатель Fortran и копирует данные, а второй – освобождает этот указатель. Каждый вызов процедуры распределения должен соответствовать соответствующему вызову процедуры освобождения, передавая один и тот же указатель. Что-то вроде:

  SUBROUTINE GET_POINTER(ARRAY_PTR) BIND(C, NAME='get_pointer') ! C signature: void get_pointer(double **); USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_LOC, C_PTR, C_DOUBLE TYPE(C_PTR), INTENT(OUT) :: ARRAY_PTR REAL(C_DOUBLE), POINTER :: HESS_COPY(:,:) ! See also the SOURCE= specifier. ALLOCATE(HESS_COPY(SIZE(HESS,1), SIZE(HESS,2)) HESS_COPY = HESS ARRAY_PTR = C_LOC(HESS_COPY) END SUBROUTINE GET_POINTER SUBROUTINE RELEASE_POINTER(ARRAY_PTR) BIND(C, NAME='release_pointer') ! C signature: void release_pointer(double*); USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_DOUBLE TYPE(C_PTR), INTENT(IN), VALUE :: ARRAY_PTR REAL(C_DOUBLE), POINTER :: HESS_COPY(:,:) CALL C_F_POINTER(ARRAY_PTR, HESS_COPY, SHAPE(HESS)) DEALLOCATE(HESS_COPY) END SUBROUTINE RELEASE_POINTER