【问题标题】:Allocating memory in C for a fortran array在 C 中为 fortran 数组分配内存
【发布时间】:2021-06-04 23:46:51
【问题描述】:

我是 fortran 编程的新手。我有一个 3 维数组,声明如下

REAL*4, DIMENSION(:,:,:), ALLOCATABLE :: a1

我想通过引用 C 或 C++ 函数来传递数组,并在 C 中而不是在 fortran 中分配内存。是否有可能或者我在理解 fortran 中的数组概念时错了?

【问题讨论】:

  • 当然应该可以,但是你需要很好地理解这种fortran数组是如何实现的。
  • 谢谢@BasileStarynkevitch
  • 如果that可以帮助...

标签: c fortran


【解决方案1】:

这取决于,但可能不是,取决于您的情况。如果您根据问题标签仅限于 Fortran 90,则绝对不是便携式方式。

请注意,您的 Fortran 变量是 ALLOCATABLE。

正如您所问的,它需要实现最近发布的技术规范 (TS 29113:2012) 的 Fortran 编译器(和配套的 C 编译器)。您处于语言开发的最前沿 - 当前支持此 TS 的编译器数量很少(如果不是零的话)。预计此 TS 的内容会被纳入到 Fortran 标准的下一次修订中,所以过几年再试一次,你可能会有更好的运气。

但请注意,TS 限制了 C 为 Fortran 进行分配的方式 - 基本上 C 代码需要调用 Fortran 编译器提供的例程。您不能使用任何旧的分配器(包括 malloc),所以这仍然可能不适合。

但是,Fortran 2003 增加了在 C 中完成 Fortran POINTER(不是您所要求的可分配)内存分配的可能性。C 代码返回一个指向适当大小的内存块的指针, Fortran 过程作为 C_PTR 类型的对象接收回来。然后,Fortran 代码调用过程 C_F_POINTER 以将 C_PTR 对象中的 C 地址与 Fortran 指针相关联。 C_PTR 和 C_F_POINTER 是内部 ISO_C_BINDING 模块中的实体。

一个粗略的例子:

USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER
INTERFACE
  FUNCTION get_some_memory() BIND(C, NAME='get_some_memory')
    IMPORT :: C_PTR
    TYPE(C_PTR) :: get_some_memory
  END FUNCTION get_some_memory
END INTERFACE

! REAL*4 isn't Fortran.  Correct syntax below.
REAL(4), POINTER, DIMENSION(:,:,:) :: a1

! get_some_memory would typically be told how much memory was needed.
! n1, n2, n3 are integer with the relevant extents for each dimension.
CALL C_F_POINTER(get_some_memory(), a1, [n1,n2,n3])

! Equivalent call to free required at some later stage.


void *get_some_memory()
{
  return malloc(some_number_of_bytes);   
} 

除此之外,您还可以玩一些依赖于处理器(即不可移植)的技巧,但它们不属于标准语言。

【讨论】:

  • 谢谢@IanH.. 你的例子很有帮助.. 幸运的是我安装的 gcc 编译器支持你提到的技术规范 (T229113:2012) 并且它可以工作..
  • 该示例仅使用 Fortran 2003。gfortran 开发人员仍在努力支持 TS 29113。
【解决方案2】:

正如answer by IanH 所说, 技术规范 TS 29113 允许在 C 函数中执行 Fortran 可分配对象的分配,这对 Fortran 方面具有持久影响。

由于较早的答案是书面的,因此对此 TS 的支持有所增加(尽管它仍然不大,并且我们将在此答案中使用的 C 描述符部分明显不受当前 gfortran 的支持)。这意味着现在可能值得写一个例子。此示例在 Fortran 2008 下无效。

我们考虑由CFI_desc_t(来自源文件ISO_Fortran_binding.h)和C 函数CFI_allocate 给出的C 结构。

假设我们对 rank-3 数组的分配发生在与以下 Fortran 接口兼容的 C 过程中

interface
   subroutine allocating_sub(x) bind(c)
     use, intrinsic :: iso_c_binding, only : c_float
     real(c_float), allocatable :: x(:,:,:)
   end subroutine allocating_sub
end interface

我们注意到这样的 Fortran 接口与 C 函数原型兼容,该原型具有作为指向 CFI_cdesc_t 的指针的形式参数。

将此 C 函数定义为(带有幻数,并且缺少很多 其他合理的检查等):

#include "ISO_Fortran_binding.h"

void allocating_sub(CFI_cdesc_t* x) {
  if (x->base_addr) CFI_deallocate(x);
  CFI_index_t lower[3]={-1, 5, 1};
  CFI_index_t upper[3]={20, 9, 5};
  CFI_allocate(x, lower, upper, 0);
}

CFI_index_t 是“能够表示两个指针相减结果的标准有符号整数类型的 typedef 名称”。

分配发生在CFI_allocate 函数中,该函数在这里分配x,其下限和上限由明显的变量给出。在我们的例子中,最后一个数字 (0) 被忽略,x 不对应于 Fortran 字符类型。

为了完成这个例子,使用 Fortran 程序:

  use, intrinsic :: iso_c_binding, only : c_float
  implicit none

  interface
     subroutine allocating_sub(x) bind(c)
       import c_float
       real(c_float), allocatable :: x(:,:,:)
     end subroutine allocating_sub
  end interface

  integer i
  real(c_float), allocatable :: x(:,:,:)

  allocate(x(6,2,5))
  print 1, (LBOUND(x,i), UBOUND(x,i), i=1,3)

  call allocating_sub(x)
  if (allocated(x)) print 1, (LBOUND(x,i), UBOUND(x,i), i=1,3)

1 format ("x has been allocated like x(",2(I0,":",I0,","),I0,":",I0,").")

end

这个例子有点不简单,因为它还显示了CFI_deallocate 函数,尽管我们在 C 端有这个额外的级别,但我们仍然可以通过组件 @ 访问该 C 函数中的内存。 987654335@。如果未分配 Fortran 变量,则这是一个空指针;否则它是 Fortran 数组的第一个元素的 C 地址,只要至少有一个元素。

更多细节可以在 TS 文档或即将发布的 Fortran 标准草案中找到。

【讨论】:

    【解决方案3】:

    自 F90 以来,ALLOCATABLE 大部分时间都在 Fortran 中,因此非常“古老”(在 25 岁以上还不是最前沿的)。

    但是,只是出于好奇,为什么 c 端需要 mem 分配?如果您需要在 F 端使用该数组,则需要在某个时间点在 F 端分配或声明一些空间。

    此外,您实际上可能不需要 Allocatable。您可能会发现自动数组更方便。例如:

    Subroutine sr(ArrayA, Answer, iJunk)
        Real(4), Intent(In)    :: ArrayA(:,:)   ! immediately has correct dims etc
        Integer, Intent(In)    :: iJunk
        Real(4), Intent(Out)   :: Answer
        !
        ! locals
        !
        Real(4)                :: ArrayB(Size(ArrayA, Dim = 1))   ! AUTOMATIC array declared "on the fly" with order of that of the leading dimension of ArrayA, for example.
        Real(4)                :: ArrayC(iJunk)                   ! another way to create "dynamic" arrays
        !
        ! ... do stuff
    End Subroutine sr          ! All automatic (and allocatables) are cleared/destroyed "automatically" since F95, with F90 you should manually DeAllocate allocated arrays.
    

    这可以在您调用 c 之前或之后完成,因此如果 c 返回所需的尺寸,您可以“自动”或按套装分配。

    不确定这是否有帮助,因为我不确定我是否掌握了您目标的本质。

    另外,值得深思的是,虽然 ISO 绑定通常是一个好主意,但它可能会引起一些头痛。例如,在许多情况下(使用 ISO),您必须将字符串作为单个字符数组传递,c.f.具有 n 个字符的单个字符串。这可能有点令人遗憾,特别是如果您有一个字符串数组等。混合语言位可以在没有 ISO 的情况下完成,在某些情况下“成本”要低得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-27
      • 2014-12-09
      • 1970-01-01
      • 2015-06-09
      • 2016-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多