我正在看一个稍老的带有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/