6

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

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

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

4

3 回答 3

4

这取决于您的情况,但可能不是。如果您根据问题标签仅限于 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);   
} 

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

于 2013-05-05T21:31:04.760 回答
3

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

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

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

假设我们对 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 端有这个额外的级别,但我们仍然可以通过 component 访问该 C 函数中的内存x->base_addr。如果未分配 Fortran 变量,则这是一个空指针;否则它是 Fortran 数组的第一个元素的 C 地址,只要至少有一个元素。

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

于 2016-06-15T13:51:40.873 回答
1

自 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),您必须将字符串作为单个字符数组传递,例如带有 n 个字符的单个字符串。这可能有点令人遗憾,特别是如果您有一个字符串数组等。混合语言位可以在没有 ISO 的情况下完成,在某些情况下“成本”要低得多。

于 2014-02-18T15:10:23.493 回答