我理解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)
答案 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)
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)
会导致分段错误,因为rows
和cols
未正确传递。)
此外,为了便于检查数组元素的对应关系,我将测试值修改为
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提供的建议)。