我正在看一个稍老的带有C调用的Fortran程序,该程序适用于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

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;
}

编译

可以使用GNU编译器使用32位的-m32(此处没有问题)和64位的-m64编译该程序。编译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

而其余的编译和链接很好,并且程序可以运行:
$ 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

问题

虽然我看到calloc返回的地址可以容纳4字节整数的数据限制,但是calloc是否有返回较大整数的地址的风险?使用(intptr_t)进行强制转换将使编译警告静音,但是我怀疑如果尝试将指针强制转换为截断的地址,它将截断任何更高的位和“分段错误”。这样对吗?

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

最佳答案

是的,存在潜在的咬人问题。如果您希望代码在面对编译器和平台更改时变得健壮,那么您应该做很多事情,其中​​大多数依赖于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一侧的接口(interface)块中使用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

并在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;
}

关于c - 将Fortran/C程序从32位升级到多体系结构,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15473004/

10-11 22:43
查看更多