将Fortran/C程序从32位升级到多架构

Mik*_*e T 2 c fortran pointers 32bit-64bit fortran-iso-c-binding

我正在寻找一个稍微陈旧的Fortran程序,其C调用适用于32位系统,但会引发警告和对64位编译器的担忧.程序将C指针的地址存储为动态分配的内存int,在Fortran端共享为INTEGER.我担心的是,在64位整数系统中,来自C指针的强制转换可能比可以存储为int/的更大INTEGER.我用两个文件将现有程序简化为此示例.

Fortran语言: this.f

        program this
        integer,pointer :: iptr
        allocate(iptr)
        call that_allocate(iptr)
        write(*,'(A, Z12)') 'Fortran: iptr address', iptr
        call that_assemble(iptr)
        call that_free(iptr)
        end program this
Run Code Online (Sandbox Code Playgroud)

C: that.c

#include <stdlib.h>
#include <stdio.h>

typedef struct data {
    int a;
    float b;
} data;

void that_allocate_(int* iptr)
{
    data *pData = calloc(1, sizeof(data));
    *iptr = (int)pData;
    printf("C: Allocated address %p (or %d)\n", pData, pData);
    return;
}

void that_assemble_(int* iptr)
{
    data *pData = (data *) *iptr;
    pData->a = 42;
    pData->b = 3.1415926;
    return;
}

void that_free_(int* iptr)
{
    data *pData = (data *) *iptr;
    printf("C: Freeing data %d and %g at %p\n", pData->a, pData->b, pData);
    free(pData);
    return;
}
Run Code Online (Sandbox Code Playgroud)

该程序可以使用GNU编译器编译-m32为32位(此处没有问题)和-m6464位编译.编译C代码会引发一些警告:

$ gcc -m64 -c that.c
that.c: In function ‘that_allocate_’:
that.c:12: warning: cast from pointer to integer of different size
that.c: In function ‘that_assemble_’:
that.c:19: warning: cast to pointer from integer of different size
that.c: In function ‘that_free_’:
that.c:27: warning: cast to pointer from integer of different size
Run Code Online (Sandbox Code Playgroud)

而剩下的编译和链接很好,程序工作:

$ gfortran -m64 -o prog this.f that.o
$ ./prog 
C: Allocated address 0x1130b40 (or 18025280)
Fortran: iptr address     1130B40
C: Freeing data 42 and 3.14159 at 0x1130b40
Run Code Online (Sandbox Code Playgroud)

问题

虽然我看到calloc返回的地址可以在4字节整数的数据限制范围内,但是存在calloc返回具有更大整数的地址的风险吗?使用Cast (intptr_t)会使编译警告静音,但我怀疑如果试图将指针强制转换为截断的地址,它将截断任何更高的位和"Segmentation fault".它是否正确?

我该怎么办?修复程序是否需要继续使用Fortran代码?

Ian*_*anH 6

是的,存在潜在的比特问题.如果您希望代码在编译器和平台更改时变得健壮,那么您应该做很多事情,其中​​大部分都依赖于Fortran 2003的C互操作性功能.这些语言功能最近得到了gfortran的支持.并且最积极地维护Fortran编译器.

从您的示例中不清楚Fortran代码是否真的需要知道指向data结构的指针的值作为整数(在您的示例中,您打印此值,但我怀疑这只是用于调试).如果Fortran代码只需要将指针视为不透明句柄,那么ISO_C_BINDING内部模块中的C_PTR类型就等同于C指针.如果由于某种原因,Fortran代码确实需要将指针的值知道为整数,那么类型C_INTPTR_T的整数(再次来自ISO_C_BINDING内部模块)是合适的.更进一步,如果您希望fortran代码能够使用实际结构本身,那么您可以定义BIND(C)派生类型并以各种方式使用它.

此外,您的C代码假定Fortran编译器使用某种调用约定,包括修改过程名称以形成链接器名称的方式.您可以在Fortran端的接口块中使用BIND(C,NAME ='xxx')属性来指定Fortran编译器使用与其配套C编译器兼容的调用约定并指定过程的C名称.

请注意,就您的示例的其余部分而言,整数及其后续分配的POINTER声明是不相关的.

全部(自由形式,已经有一段时间,因为固定形式是适当的):

PROGRAM this
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_INTPTR_T
  IMPLICIT NONE
  ! Interfaces required due to BIND(C).  Also allows the Fortran 
  ! compiler to do better error checking.  Note that the default 
  ! binding label is a lowercase variant of the Fortran name, but
  ! we specify it explicitly here anyway for clarity.
  INTERFACE
    SUBROUTINE that_allocate(the_c_ptr)  &
        BIND(C,NAME='that_allocate')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! Note passing a pointer by reference.
      TYPE(C_PTR), INTENT(OUT) :: the_c_ptr
    END SUBROUTINE that_allocate

    SUBROUTINE that_assemble(the_c_ptr)  &
        BIND(C,NAME='that_assemble')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! Note passing a pointer by value.
      TYPE(C_PTR), INTENT(IN), VALUE :: the_c_ptr
    END SUBROUTINE that_assemble

    SUBROUTINE that_free(the_c_ptr)  &
        BIND(C,NAME='that_free')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      ! Note passing a pointer by value.
      TYPE(C_PTR), INTENT(IN), VALUE :: the_c_ptr
    END SUBROUTINE that_free
  END INTERFACE

  TYPE(C_PTR) :: the_c_ptr
  CALL that_allocate(the_c_ptr)
  ! Use transfer to convert the C address to an integer value.
  PRINT "('Fortran: ptr address',Z12)",  &
      TRANSFER(the_c_ptr, 0_C_INTPTR_T)
  CALL that_assemble(the_c_ptr)
  CALL that_free(the_c_ptr)
END PROGRAM this
Run Code Online (Sandbox Code Playgroud)

并简化C方面:

#include <stdlib.h>
#include <stdio.h>

typedef struct data {
  int a;
  float b;
} data;

void that_allocate(data** pData)
{
    *pData = (data*) calloc(1, sizeof(data));
    printf("C: Allocated address %p\n", *pData);
    return;
}

void that_assemble(data* pData)
{
    pData->a = 42;
    pData->b = 3.1415926;
    return;
}

void that_free(data* pData)
{
    printf("C: Freeing data %d and %g at %p\n", pData->a, pData->b, pData);
    free(pData);
    return;
}
Run Code Online (Sandbox Code Playgroud)