【问题标题】:Incorrect value returned by ISO_C_binding "c_intptr_t" in Fortran / Mac OS XFortran / Mac OS X 中 ISO_C_binding "c_intptr_t" 返回的值不正确
【发布时间】:2018-12-21 18:01:38
【问题描述】:

我有以下 Fortran 函数 create_pointer 调用名为 create_pointer 的 C 函数,该函数创建一个指针并返回其地址:

FUNCTION create_pointer() RESULT(c_intptr_t) BIND(C, name = "create_pointer")
    USE, INTRINSIC :: iso_c_binding, ONLY: c_intptr_t
END FUNCTION

作为参考,C 函数create_pointer 如下:

intptr_t create_pointer(void)
{
    void *ptr;
    intptr_t address;

    ptr = malloc(100);
    address = (intptr_t) ptr;
    printf("FROM C: address %p\n", address)
    return address;
}

现在,当编译(使用 GCC 4.9)并在 Mac OS X 64 位中执行以下 Fortran 程序时:

PROGRAM

    INTEGER(c_intptr_t) :: address

    address = create_pointer()

    WRITE(*, '(Z8)') "FROM FORTRAN:", address

END PROGRAM

它给出了一个可能与此类似的意外输出:

FROM C: address 0x7f8870c3ee00
FROM FORTRAN: address 70C3EE00

为什么在 C 中显示的地址与在 Fortran 中显示的地址不同?我会假设 ISO_C_binding c_intptr_t 将保留这两种语言之间的正确值。此外,可以注意到,Fortran 从 C 接收的地址包含在其中(换句话说,初始值 0x7f88 被丢弃)。是不是因为 64 位地址计数中只有前 48 位,而 ISO_C_binding 丢弃了后 16 位?

仅供参考,这次在 Ubuntu 64 位编译(使用 GCC 4.9)并运行相同的 Fortran 程序时,输出是一致的(即 C 和 Fortran 显示的地址相同)。

【问题讨论】:

  • 在 Mac 上,您确定您使用的是实际的 GCC 而不是 Clang(因为默认情况下链接到 gccg++ 命令)?
  • 是的,它是 GCC。运行 gfortran -vgcc -v 时,它们都返回 gcc version 4.9.4 (MacPorts gcc49 4.9.4_3)
  • 70c3ee00 是地址的低 32 位。这表明 c_intptr_t 被错误地定义为 32 位而不是 64 位的数量。我不知道你如何在 Fortran 中声明一个 64 位整数,但如果你将 address 的 Fortran 端声明和 create_pointer 上的 RESULT 声明更改为任何内容,你现在得到正确的值了吗?
  • @zwol,通常c_intptr_t 如果编译为 32 位,则自动具有 4 个字节的大小,如果编译为 64 位,则自动具有 8 个字节的大小,所以我认为问题不存在。尽管如此,我尝试过使用c_int64_tc_long_long,但它们都没有工作(即它们显示与c_intptr_t 相同)。
  • 但现在我发现了问题:请将implicit none 添加到您的 Fortran 程序中。你的函数create_pointer 被假定返回一个默认的真实结果,因为你实际上并没有包含你在主程序中给出的接口。

标签: c macos gcc fortran


【解决方案1】:

你的错误在这里:

FUNCTION create_pointer() RESULT(c_intptr_t) BIND(C, name = "create_pointer")

您应该在函数声明的result 部分中放置一个变量名。然后你可以使用你放在函数体中的名称,函数终止时它的值将是函数返回的结果值。

在您的代码中,您输入了名称c_intptr_t,这与您刚刚从内部模块导入的名称一致,并且您有效地覆盖了它。我不认为那是意图。

另外,正如@francescalus 所说,您没有implicit none,因此您让Fortran 的默认implicit rules 选择结果变量的类型;它选择默认实数(因为变量名以字母 C 开头) - 这就是指针大小为 32 位的原因。

c_intptr_t 是内部模块中的 constant iso_c_binding 旨在用作获得系统正确指针大小的可互操作整数的类型参数

您可以将结果中的变量名称更改为ptr,然后将您的函数声明更改为:

function create_pointer() result(ptr) bind(C, name = "create_pointer")
  use, intrinsic :: iso_c_binding, only: c_intptr_t
  implicit none
  integer(c_intptr_t) :: ptr
end function

【讨论】:

  • 关于“有效覆盖”:不允许两次使用,而不是函数结果的名称隐藏使用的实体的名称。但这很好,因为该接口不包含在任何编译的代码中(我们可以看到)。
  • 这就是我所说的覆盖:隐藏。
  • 但这不是合法的隐藏:它违反了 Fortran 要求。你说它是错误的是完全正确的,但它是完全错误的,而不是不是预期的。
【解决方案2】:

基于上述解决方案和 cmets,我设法通过执行以下操作解决了该问题:

integer(c_intptr_t) function create_pointer() bind(C, name = "create_pointer")
    use, intrinsic :: iso_c_binding, only: c_intptr_t
end function

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多