在C中为Fortran分配内存分配

时间:2014-05-27 14:09:52

标签: c++ fortran mixed-code allocatable-array

我们正在尝试用C ++接管传统Fortran代码(+100,000行代码)的内存分配,因为我们使用C库在群集上分区和分配分布式内存。可分配变量在模块中定义。当我们调用使用这些模块的子程序时,索引似乎是错误的(换一个)。但是,如果我们将相同的参数传递给另一个子例程,我们就会得到我们期望的结果。以下简单示例说明了该问题:

hello.f95:

 MODULE MYMOD
    IMPLICIT NONE
    INTEGER, ALLOCATABLE, DIMENSION(:) :: A
    SAVE
  END MODULE

  SUBROUTINE TEST(A)
    IMPLICIT NONE
    INTEGER A(*)
    PRINT *,"A(1): ",A(1)
    PRINT *,"A(2): ",A(2)
  END

  SUBROUTINE HELLO()
    USE MYMOD
    IMPLICIT NONE
    PRINT *,"A(1): ",A(1)
    PRINT *,"A(2): ",A(2)
    CALL TEST(A)
  end SUBROUTINE HELLO

的main.cpp

extern "C" int* __mymod_MOD_a; // Name depends on compiler
extern "C" void hello_();      // Name depends on compiler

int main(int args, char** argv)
{
  __mymod_MOD_a = new int[10];
  for(int i=0; i<10; ++i) __mymod_MOD_a[i] = i;
  hello_();
  return 0;
}

我们正在编译:

gfortran -c hello.f95; c++ -c main.cpp; c++ main.o hello.o -o main -lgfortran;

运行./main的输出是

 A(1):            1
 A(2):            2
 A(1):            0
 A(2):            1

正如您所看到的,A的输出是不同的,尽管两个子程序都打印了A(1)和A(2)。因此,似乎HELLO从A(0)而不是A(1)开始。这可能是因为从未在Fortran中直接调用过ALLOCATE,因此它不知道A的界限。任何解决方法?

3 个答案:

答案 0 :(得分:3)

ISO_C_BINDING&#34;等效&#34;代码:

c ++代码:

extern "C" int size;
extern "C" int* c_a;
extern "C" void hello();
int main(int args, char** argv)
{
  size = 10; 
  c_a = new int[size];
  for(int i=0; i<size; ++i) c_a[i] = i; 
  hello(); 
  return 0;
}

fortran代码:

  MODULE MYMOD
    USE, INTRINSIC :: ISO_C_BINDING
    IMPLICIT NONE
    INTEGER, BIND(C) :: SIZE
    TYPE (C_PTR), BIND(C) :: C_A 
    INTEGER(C_INT), POINTER :: A(:)
    SAVE
  END MODULE

  SUBROUTINE TEST(A)
    IMPLICIT NONE
    INTEGER A(*)
    PRINT *,"A(1): ",A(1)
    PRINT *,"A(2): ",A(2)
  END 

  SUBROUTINE HELLO() BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING
    USE MYMOD
    IMPLICIT NONE
    CALL C_F_POINTER(C_A,A,(/SIZE/))
    PRINT *,"A(1): ",A(1)
    PRINT *,"A(2): ",A(2)
    CALL TEST(A)
  END SUBROUTINE

输出:

A(1):            0
A(2):            1
A(1):            0
A(2):            1

答案 1 :(得分:1)

Fortran数组伪参数始终从子例程中定义的下限开始。通话期间不保留下限。因此,A中的参数TEST()将始终从1开始。如果您希望从42开始,您必须:

INTEGER A(42:*)

关于分配,你正在玩火。为此使用Fortran指针要好得多。

integer, pointer :: A(:)

然后,您可以通过

将数组设置为指向C缓冲区
use iso_c_binding

call c_f_pointer(c_ptr, a, [the dimensions of the array])

其中c_ptr属于type(c_ptr),可与void *互操作,该iso_c_binding也来自{{1}}。

<强> --- ---编辑 一旦我看到@Max la Cour Christensen实现了我上面描述的内容,我就会发现我误解了代码的输出。描述符确实是错误的,尽管我没有写任何错误。上述解决方案仍然适用。

答案 2 :(得分:0)

fortran数组的内部表示与C / C ++中使用的表示非常不同。

Fortran使用以指向数组数据的指针开头的描述符,后跟元素类型大小,维数,一些填充字节,内部32/64位字节序列,指示各种标志,如指针,目标,可分配,大多数这些标志都没有记录(至少在我使用的ifort中),最后是一系列记录,每个记录描述相应维度中元素的数量,元素之间的距离,等。

要从fortran'看'一个外部创建的数组,你需要在C / C ++中创建这样的描述符,但是,它并没有就此结束,因为fortran也在它之前的每个子程序的启动代码中复制它们根据“in”,“out”,“inout”等指标以及fortran数组声明中使用的其他指标,获取您的第一个语句。

使用特定大小声明的类型中的数组(同样在ifort中)映射到相同类型和数量的元素的相应C结构成员,但指针和可分配类型成员实际上是需要初始化为的类型中的描述符所有字段中的正确值,以便fortran可以“看到”可分配的值。这是最棘手和危险的,因为fortran编译器可能会以未记录的方式生成数组的复制代码以进行优化,但它需要“看到”所有涉及的fortran代码才能这样做。任何来自fortran域的东西都是未知的,并且可能导致意外行为。

最好的办法是看看gfortran是否支持iso_c_binding这样的东西并为你的fortran代码定义这样的接口,然后使用iso_c_binding内在函数将C_PTR指针映射到类型,数组等的fortran指针。

您还可以将指针传递给char的一维数组及其大小,这对于字符串很有用,只要大小通过值作为最后一个参数传递(同样,编译器和编译器标志依赖)

希望这有帮助。

编辑:在弗拉基米尔的评论之后将'ifort的iso_c_binding'更改为'iso_c_binding - 谢谢!