在C中为fortran数组分配内存

时间:2013-05-05 14:11:24

标签: c fortran

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

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

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

3 个答案:

答案 0 :(得分:3)

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

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

正如您所问,它需要Fortran编译器(和配套C编译器)来实现最近发布的技术规范(T229113:2012)。您正处于语言开发的最前沿 - 目前支持此TS的编译器数量很少(如果不是零)。预计此TS的内容将被纳入Fortran标准的下一版本,因此请在几年后再试一次,您可能会有更好的运气。

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

然而,Fortran 2003增加了Fortran POINTER的内存分配(不是你所要求的可分配的)在C中完成的可能性.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);   
} 

除此之外,您还可以使用与处理器相关的(即非便携式)技巧,但它们不在标准语言中。

答案 1 :(得分: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函数中的内存组件x->base_addr。如果没有分配Fortran变量,这是一个空指针;否则它是Fortran数组的第一个元素的C地址,只要至少有一个元素。

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

答案 2 :(得分:1)

ALLOCATABLE自F90以来一直在Fortran中,因此非常“古老”(在25岁以上并不是很流行)。

然而,出于好奇,为什么在c端需要内存分配?如果你需要在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返回所需的dims,你可以“自动”,或者分配为诉讼。

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

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