传递分配C_PTR与Fortran数组到C [英] Passing allocated C_PTR to Fortran array to C

查看:167
本文介绍了传递分配C_PTR与Fortran数组到C的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在访问C中的阵列,这在下面的Fortran文件分配与段错误的麻烦。还有调试的一些文物,如事实,文件写入不写任何有意义的东西,我初始化变量 I ,我从来没有使用。

不过,我已经找到了以下内容:


  • 未初始化 I (但仍然宣称它):无段错误

  • 不打开该文件位于C:没有段错误

  • 不打印 HESS (不是 HESS_COPY )在code别处:没有段错误

  • 声明并初始化 I 使用不同的名称:段错误

有谁知道可能会引起这种行为?该段错误本身发生在行 ARRAY_PTR = C_LOC(HESS_COPY(1,1))。我使用的编译 gfortran GCC 与调试标志(不优化)。

的valgrind 说有一个无效的写(顶部两个文件都是我在下面显示):

 尺寸为8的写入无效
    在0xBEEA3E:get_pointer(modsparsehess.f90:34)
    通过0xA75D7A:print_hess(sparse_hessian_c.c:108)
    通过0x866C95:quench_(quench.f:316)
    通过0x7F2DBE:MC_(mc.F:368)
    通过0x4B65E2:mcruns_(mcruns.f:62)
    由0x459245:MAIN__(main.F:430)
    通过0x45A33F:主(main.F:21)
  地址87H的不stack'd,malloc分配或(最近)free'd

C文件

 的#include<&stdio.h中GT;无效get_pointer(双**粗麻布);无效print_hess为(int * arr_size){
   //创建一个指针来处理麻袋
   双**粗麻布;
   INT I;
   I = 0;
   get_pointer(麻袋);   的printf(%8.3f,**粗麻布);
   //打开一个用于写入的文件
   FILE * FPTR =的fopen(hessian_out,W);
   //打印黑森州
   fprintf中(FPTR,\\ n);
   FCLOSE(FPTR);
}

Fortran语言文件

 模块MODSPARSEHESS
USE,INTRINSIC :: ISO_C_BINDING
使用MODHESS,ONLY:HESS接口
   SUBROUTINE PRINT_HESSIAN(尺寸)BIND(C,NAME ='print_hess')
      USE,INTRINSIC :: ISO_C_BINDING
      INTEGER(c_int的)::尺寸
   END SUBROUTINE PRINT_HESSIAN
后端接口CONTAINS   SUBROUTINE GET_POINTER_IN_C(ARRAY_PTR)BIND(C,NAME ='get_pointer')
   ! Ç签名:无效get_pointer(双**粗麻布);
      USE,INTRINSIC :: ISO_C_BINDING
      隐式NONE   !参数
      TYPE(C_PTR),INTENT(OUT):: ARRAY_PTR
   !局部变量
      REAL(C_DOUBLE),外形尺寸(:, :),&安培;
      分配的,TARGET :: HESS_COPY   !复制成麻袋HESS_COPY
      IF(.NOT。ALLOCATED(HESS_COPY))THEN
        ALLOCATE(HESS_COPY(SIZE(HESS,1),尺寸(HESS,2)))
      万一
      HESS_COPY(:, :) = HESS(:, :)   !现在得到的指针
      ARRAY_PTR = C_LOC(HESS_COPY(1,1))   END SUBROUTINE GET_POINTER_IN_C
前端模块MODSPARSEHESS


解决方案

变量 HESS_COPY 是本地的,未保存,可分配的Fortran语言程序的变量 GET_POINTER_IN_C

因此​​,每当程序开始执行它始终是未分配的。其分配状态该过程中的第一个可执行语句的测试是因此多余的。

因此​​也,在未保存的局部变量自动释放过程的执行结束。因此朝向过程结束时C_LOC参照获取一个对象,它是即将停止现有的地址

在C code,那么与对象不存在的地址作品,并计划将失败。

如果在 HESS_COPY 变量保存本地或保存模块变量,将继续过程调用之间存在。

(所有模块变量都保存为2008年的Fortran,previous语言版本正式要求SAVE明确具体的,如果模块没有不断地在活动范围内引用相关模块的变量。)

(顺便说一句,语言的规则,在2003年的Fortran,也意味着分配的测试中,分配语句和随后的分配可以简单地通过单个语句 HESS_COPY = HESS被替换


此外,在C code,试图被做在不存在返回一个指针的信息。在原来的code,粗麻布被声明为指针的指针翻番 - 注意间接的两个层次。如果没有某种形式的初始化间接的第一级将在内存中指向随机时,Fortran的code将被努力的结果存储在该随机位置。

作为一种替代方法,可以考虑:

 的#include<&stdio.h中GT;无效get_pointer(双**粗麻布);无效print_hess为(int * arr_size){
   //指向double(一个间接层)。
   双*粗麻布;   //传递指针的地址。
   get_pointer(安培;粗麻布);   //打印对准了双重的价值。
   的printf(%8.3f \\ n,*麻袋);   //打印阵列中的下一个双值
   //(假设有多于一个)。
   的printf(%8.3f \\ n,*(黑森州+ 1));
   //(或等价,`麻袋[1]`)
}


弗拉基米尔·f由于意见中提到的F​​ortran指针指向的方法需要两个FORTRAN过程 - 一个类似于一个你有分配Fortran指针,并将数据复制,这将释放该指针的第二位。该分配过程的每个调用需要用的释放过程的调用相应匹配,传递相同的指针。沿着线的东西:

  SUBROUTINE GET_POINTER(ARRAY_PTR)BIND(C,NAME ='get_pointer')
   ! Ç签名:无效get_pointer(双**);
      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(:, :)      !又见SOURCE =说明。
      ALLOCATE(HESS_COPY(SIZE(HESS,1),尺寸(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')
   ! Ç签名:无效release_pointer(双*);
     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,形状(HESS))
     DEALLOCATE(HESS_COPY)
   END SUBROUTINE RELEASE_POINTER

I'm having trouble with segfaults from accessing an array in C, which is allocated in the Fortran file below. There are a few artefacts of debugging, such as the fact that the file writes don't write anything meaningful and I initialise a variable i that I never use.

However, I've found the following:

  • Not initialising i (but still declaring it): no segfault
  • Not opening the file in C: no segfault
  • Not printing HESS (not HESS_COPY) elsewhere in the code: no segfault
  • Declaring and initialising i with a different name: segfault

Does anyone know what might give rise to this behaviour? The segfault itself occurs at the line ARRAY_PTR = C_LOC(HESS_COPY(1, 1)). I am compiling using gfortran and gcc with debug flags (no optimisation).

valgrind says that there is an invalid write (the top two files are the ones I show below):

 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 file

#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 file

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

解决方案

The variable HESS_COPY is a local, unsaved, allocatable variable of the Fortran procedure GET_POINTER_IN_C.

Consequently, whenever the procedure begins execution it is always unallocated. The test of its allocation status in the first executable statement of that procedure is therefore superfluous.

Consequently also, at the end of execution of the procedure that unsaved local variable is automatically deallocated. The C_LOC reference towards the end of the procedure therefore obtains the address of an object that is about to cease existing.

The C code then works with the address of an object that does not exist, and the program fails.

If the HESS_COPY variable was saved local or saved module variable it would continue to exist between procedure invocations.

(All module variables are saved as of Fortran 2008, previous language revisions formally required explicit specific of SAVE for the relevant module variables if the module was not continuously being referenced in an active scope.)

(As an aside, the rules of the language, as of Fortran 2003, also mean that the allocated test, the allocate statement and the subsequent assignment can simply be replaced by the single statement HESS_COPY = HESS.)


Further, in the C code, an attempt is being made to return information in a pointer that does not exist. In the original code, hessian is declared as a pointer to a pointer to double - note the two levels of indirection. Without some sort of initialization the first level of indirection will be pointing "randomly" in memory, the Fortran code will then be trying to store its result in that random location.

As an alternative, consider:

#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]`)
}


The Fortran pointer method referred to by Vladimir F in the comments requires two Fortran procedures - one similar to the one that you have that allocates a Fortran pointer and copies the data, the second that deallocates that pointer. Each call to the allocation procedure needs to be matched with a corresponding call to the deallocation procedure, passing the same pointer. Something along the lines of:

   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

这篇关于传递分配C_PTR与Fortran数组到C的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆