试图将连续的动态二维数组从C传递给Fortran

时间:2016-12-09 11:58:33

标签: c multidimensional-array fortran language-interoperability

我理解extra care needs to be taken在C中分配内存以确保2d数组是连续的,但是当我将它传递给Fortran时,我仍然没有得到预期的结果。下面是我的尝试的玩具版本:一个main.c文件,为2d数组分配内存并为每个元素分配一个值,以及一个打印出2d数组元素的foo.f90文件。

#include <stdio.h>
#include <stdlib.h>

void foo_(double **,int *,int *);

int main() {
    int i, j, cols, rows;
    double *_x, **x;

    cols = 3;
    rows = 2;

//  Allocate memory
    _x = malloc(rows*cols*sizeof(double));
    x = malloc(cols*sizeof(double *));
    for(i=0;i<cols;i++)
        x[i] = &(_x[rows*i]);

//  Generate elements for the 2d array 
    for(i=0;i<cols;i++)
    for(j=0;j<rows;j++)
        x[i][j] = i*j;

//  Call Fortran subroutine foo
    foo_(x,&rows,&cols);

    return EXIT_SUCCESS;
}

foo.h中

subroutine foo(x,rows,cols)
    use iso_c_binding
    implicit none

    integer(c_long), intent(in)                :: rows,cols
    real(c_double), intent(in), dimension(rows,cols) :: x

    integer :: i,j

    do i = 1,cols
    do j = 1,rows
        print *, j,i,x(j,i)
    end do
    end do

end subroutine

作为输出,我期待一个数组元素列表。相反,我得到以下输出

           1           1   1.1654415706619996E-316
           2           1   1.1654423611670330E-316
Segmentation fault (core dumped)

2 个答案:

答案 0 :(得分:1)

You said so yourself: contiguous!

You do not allocate a contiguous array. To allocate one you must write:

//  Allocate memory
double *x = malloc(rows*cols*sizeof(double));

Unfortunately, you must now write the following in C to index x:

//  Generate elements for the 2d array 
for(i=0;i<cols;i++)
    for(j=0;j<rows;j++)
        *(x+j*cols+i) = i*j;

This assumes the matrix is in row-major order (row after row after row laid out contiguous in memory).

Note: in C99 there are Variable Length Arrays where the compiler manages x[i][j] properly. I do not have C99, but maybe another user can give an answer with VLAs.

答案 1 :(得分:0)

Fortran端的

foo()接收x作为显式形状数组,因此我们需要传递x的第一个元素的地址。因此,我们将原型修改为

void foo_( double *, int *, int * );

并将第一个元素的地址作为

传递
foo_( _x, &rows, &cols );
// or
// foo_( &( _x[0] ), &rows, &cols );
// or
// foo_( &( x[0][0] ), &rows, &cols );

另外,我们可能需要使用integer(c_int)而不是integer(c_long)来匹配C端的int,这样

integer(c_int), intent(in) :: rows,cols

(在我的计算机上,使用integer(c_long)会导致分段错误,因为rowscols未正确传递。)

此外,为了便于检查数组元素的对应关系,我将测试值修改为

for(i=0;i<cols;i++)
for(j=0;j<rows;j++)
    x[i][j] = (i+1) + 1000*(j+1);

并将打印语句插入到Fortran代码中

print *, "rows = ", rows
print *, "cols = ", cols

然后,我的电脑上的GCC-6(OSX 10.9)给出了

 rows =            2
 cols =            3
           1           1   1001.0000000000000     
           2           1   2001.0000000000000     
           1           2   1002.0000000000000     
           2           2   2002.0000000000000     
           1           3   1003.0000000000000     
           2           3   2003.0000000000000

作为旁注,以下似乎也有效(而不是手动创建x):

_x = malloc( rows * cols * sizeof(double) );

typedef double (*Array)[ rows ];
Array x = (Array) _x;

// set x[i][j] the same way

foo_( _x, &rows, &cols );
// or
// foo_( &( x[0][0] ), &rows, &cols );

但我不确定Array的使用是否符合标准......(在C中)。

[编辑]

使用现代C,似乎可以将x直接声明为具有动态大小的矩形数组,并在堆上分配内存,这样:

double (* x)[rows] = malloc( cols * rows * sizeof(double) );

// or
// double (* x)[rows] = malloc( sizeof( double[cols][rows] ) );

(请注意,变量名称“cols”和“rows”指的是Fortran端的变量名,因此它们在C端看起来与直觉相反。)

使用此x,我们可以按照与上述相同的方式调用foo_(),即

// set x[i][j] via loops

foo_( &( x[0][0] ), &rows, &cols );

请参阅Jens Gustedts' blog(“请勿使用假矩阵”)以获取后一种方法的详细信息(并感谢@Olaf提供的建议)。