我在访问C中的数组时遇到了段错误问题,这个数组是在下面的Fortran文件中分配的。有一些调试的假象,例如文件写入没有写任何有意义的事实,我初始化了一个我从未使用过的变量i
。
但是,我发现了以下内容:
i
(但仍然声明):no segfault HESS
(不是HESS_COPY
):no segfault i
: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 <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);
}
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
答案 0 :(得分:5)
变量HESS_COPY
是Fortran过程GET_POINTER_IN_C
的本地未保存可分配变量。
因此,无论何时程序开始执行,它始终是未分配的。因此,在该过程的第一个可执行语句中对其分配状态的测试是多余的。
因此,在执行过程结束时,会自动释放未保存的局部变量。因此,程序结束时的C_LOC引用将获得即将停止存在的对象的地址。
然后,C代码使用不存在的对象的地址,程序失败。
如果HESS_COPY
变量保存了本地或保存的模块变量,它将继续存在于过程调用之间。
(所有模块变量都保存在Fortran 2008中,如果模块未在活动范围内连续引用,则以前的语言修订版正式要求明确说明相关模块变量的SAVE。)
(另外,从Fortran 2003开始,语言规则也意味着分配的测试,分配语句和后续分配可以简单地用单个语句HESS_COPY = HESS
替换。)
此外,在C代码中,正在尝试返回不存在的指针中的信息。在原始代码中,hessian被声明为指向双指针的指针 - 注意两个间接层。如果没有某种初始化,第一级间接将“随机”指向内存,Fortran代码将尝试将其结果存储在该随机位置。
作为替代方案,请考虑:
#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]`)
}
Vladimir F在注释中引用的Fortran指针方法需要两个Fortran过程 - 一个类似于您分配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