gpt4 book ai didi

c - 将 Fortran/C 程序从 32 位升级到多体系结构

转载 作者:行者123 更新时间:2023-12-02 08:42:15 26 4
gpt4 key购买 nike

我正在查看一个稍旧的 Fortran 程序,该程序具有适用于 32 位系统的 C 调用,但会引发警告和对 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:那个.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 编译器编译,-m32 用于 32 位(这里没有问题),-m64 用于 64 位。编译 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) block 中使用 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com