matrices arreglos c fortran fortran-iso-c-binding

arreglos - Al pasar asignado C_PTR a Fortran matriz a C



arreglos en fortran (1)

Tengo problemas con segfaults para acceder a una matriz en C, que se asigna en el archivo Fortran a continuación. Hay algunos artefactos de depuración, como el hecho de que el archivo no escribe nada significativo e inicializo una variable que nunca uso.

Sin embargo, he encontrado lo siguiente:

  • No inicializar i (pero todavía lo declaro): no segfault
  • No abrir el archivo en C: sin segfault
  • No imprime HESS (no HESS_COPY ) en otro lugar del código: no segfault
  • Declarar e inicializar i con un nombre diferente: segfault

¿Alguien sabe lo que podría dar lugar a este comportamiento? La segfault se produce en la línea ARRAY_PTR = C_LOC(HESS_COPY(1, 1)) . Estoy compilando usando gfortran y gcc con indicadores de depuración (sin optimización).

valgrind dice que hay una escritura inválida (los dos mejores son los que muestro a continuación):

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

Archivo C

#include <stdio.h> 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); }

Archivo 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


La variable HESS_COPY es una variable local, no guardada y asignable del procedimiento de Fortran GET_POINTER_IN_C .

En consecuencia, cada vez que el procedimiento comienza la ejecución, siempre está sin asignar. La prueba de su estado de asignación en la primera declaración ejecutable de ese procedimiento es, por lo tanto, superflua.

En consecuencia, también, al final de la ejecución del procedimiento, la variable local no guardada se desasigna automáticamente. La referencia C_LOC hacia el final del procedimiento, por lo tanto, obtiene la dirección de un objeto que está a punto de dejar de existir.

El código C funciona entonces con la dirección de un objeto que no existe y el programa falla.

Si se HESS_COPY variable HESS_COPY local o la variable de módulo guardada, continuaría existiendo entre las invocaciones de procedimiento.

(Todas las variables del módulo se guardan a partir de Fortran 2008, las revisiones previas del lenguaje formalmente requeridas explícitamente específicas de SAVE para las variables del módulo relevante si el módulo no se referenciaba continuamente en un ámbito activo).

(Como un aparte, las reglas del lenguaje, a partir de Fortran 2003, también significan que la prueba asignada, la instrucción de asignación y la asignación posterior pueden simplemente reemplazarse por la declaración única HESS_COPY = HESS ).

Además, en el código C, se está intentando devolver información en un puntero que no existe. En el código original, hessian se declara como un puntero a un puntero para hacer doble nota en los dos niveles de indirección. Sin algún tipo de inicialización, el primer nivel de indirección señalará "aleatoriamente" en la memoria, el código Fortran intentará almacenar su resultado en esa ubicación aleatoria.

Como alternativa, considere:

#include <stdio.h> 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]`) }

El método de puntero Fortran al que se refiere Vladimir F en los comentarios requiere dos procedimientos Fortran, uno similar al que tiene que asigna un puntero Fortran y copia los datos, el segundo que desasigna ese puntero. Cada llamada al procedimiento de asignación debe coincidir con una llamada correspondiente al procedimiento de desasignación, pasando el mismo puntero. Algo como:

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