将动态二维数组从 C++ 传递到 Fortran 并返回
Passing a dynamic 2D array from C++ to Fortran and back
在 C++ 和 Fortran 之间传递一个固定的 2D 数组工作正常,但是对于我编写的用于将 2D 动态数组从 C++ 传递到 Fortran 的程序却不是这样。
C++ 端
extern "C" {void array2d_(double **, int *, int *); }
using namespace std;
int main()
{
double **array;
int nx=3;
int ny=2;
int i,j;
cout << "Passing dynamic array from C to Fortran\n";
array = (double **) malloc(nx * sizeof(double *));
if(array == NULL)
{
fprintf(stderr, "out of memory\n");
exit;
}
for(i = 0; i < nx; i++)
{
array[i] = (double *) malloc(ny * sizeof(double));
if(array[i] == NULL)
{
fprintf(stderr, "out of memory\n");
exit;
}
}
for(i = 0; i < nx; i++)
{
for(j = 0; j < ny; j++)
{
array[i][j]=i+j+i*(2+j)+4; //random initialisation
cout << "array[" << i << "][" << j << "]=" << array[i][j] << " ";
}
cout << endl;
}
array2d_(array, &nx, &ny);
for(i = 0; i < nx; i++)
free(array[i]);
free(array);
return 0;
}
fortran 方面
subroutine array2d(arr,nx_C,ny_C) bind(C,name="array2d_")
use iso_c_binding
implicit none
integer (C_INT), intent(IN) :: nx_C,ny_C !array sizes from C
real (C_DOUBLE), intent(INOUT) :: arr(ny_C,nx_C)
integer :: k,l
print *, "This is in Fortran routine..."
do k = 1,ny_C
do l=1,nx_C
print *, "arr(",k,",",l,") = ", arr(k,l)
end do
end do
end subroutine array2d
C++ 中的输出是
array[0][0]=4 array[0][1]=5
array[1][0]=7 array[1][1]=9
array[2][0]=10 array[2][1]=13
而在 Fortran 中,输出是
arr( 1 , 1 ) = 1.7994937190948764E-305
arr( 1 , 2 ) = 7.1027035167764720E-251
arr( 1 , 3 ) = 9.8813129168249309E-324
arr( 2 , 1 ) = 5.4809152658772852E-317
arr( 2 , 2 ) = 1.5475240269406953E-314
arr( 2 , 3 ) = 0.0000000000000000
所以不知何故,值没有正确传递。
主要原因是您的 C 数组是一个锯齿状数组,它是一个指向单独一维数组的指针数组,而在 Fortran 中您将参数声明为连续的二维数组。您必须在两个部分中使用相同的内容,最好在 C 中也使用连续的数组。
只需 malloc
一个大的 nx*ny
缓冲区并将指针设置为行而不是分配它们。您可以在
中查看示例
我最近在处理需要在 Fotran 中保持数组形状(不能只是一维)时使用 C_F_POINTER 取得了一些成功:
由于在从 C++ 传递动态分配的数组时传递指向值指针数组的指针(对于 2D),您可以对每个 c_ptr 使用 C_F_POINTER() 方法来帮助您在 Fortran 端填写数组。
在您的 Fortran 子例程中,您将包含数组的变量设置为行的一维数组。这包含一个指针数组,每个指针指向一维指针数组,这些指针将用您的列填充您的行。在 Fortran 方面,您需要一个 Fortran 指针的一维数组和一个将成为您的实际值的二维数组。
C++:
double** arrc = new double*[x]
for (i=0;i<x;i++)
arrc[i] = new double*[y]
some_fortran_call(arrc)
Fortran:
subroutine some_fortran_call(arrc) bind(c, name='some_fortran_call')
use iso_c_binding
type(c_ptr), intent(inout):: arrc(x)
real*8, pointer:: arrp(:)
real*8, dimension(:,:), allocatable:: arrf
allocate(arrf(x,y))
do i=1,x
call C_F_POINTER(arrc(i), arrp, [y]) ----> Each pointer in arrc points to a 1D array with y values
do j=1,y
arrf(i,j) = arrp(j) ---------> Fills in ith row with y amount of values (will do this for all x rows)
end do
end do
您仍然需要为 Fortran 数组分配新内存,但这至少节省了在 C++ 中分配新一维数组所需的内存。
在 C++ 和 Fortran 之间传递一个固定的 2D 数组工作正常,但是对于我编写的用于将 2D 动态数组从 C++ 传递到 Fortran 的程序却不是这样。
C++ 端
extern "C" {void array2d_(double **, int *, int *); }
using namespace std;
int main()
{
double **array;
int nx=3;
int ny=2;
int i,j;
cout << "Passing dynamic array from C to Fortran\n";
array = (double **) malloc(nx * sizeof(double *));
if(array == NULL)
{
fprintf(stderr, "out of memory\n");
exit;
}
for(i = 0; i < nx; i++)
{
array[i] = (double *) malloc(ny * sizeof(double));
if(array[i] == NULL)
{
fprintf(stderr, "out of memory\n");
exit;
}
}
for(i = 0; i < nx; i++)
{
for(j = 0; j < ny; j++)
{
array[i][j]=i+j+i*(2+j)+4; //random initialisation
cout << "array[" << i << "][" << j << "]=" << array[i][j] << " ";
}
cout << endl;
}
array2d_(array, &nx, &ny);
for(i = 0; i < nx; i++)
free(array[i]);
free(array);
return 0;
}
fortran 方面
subroutine array2d(arr,nx_C,ny_C) bind(C,name="array2d_")
use iso_c_binding
implicit none
integer (C_INT), intent(IN) :: nx_C,ny_C !array sizes from C
real (C_DOUBLE), intent(INOUT) :: arr(ny_C,nx_C)
integer :: k,l
print *, "This is in Fortran routine..."
do k = 1,ny_C
do l=1,nx_C
print *, "arr(",k,",",l,") = ", arr(k,l)
end do
end do
end subroutine array2d
C++ 中的输出是
array[0][0]=4 array[0][1]=5
array[1][0]=7 array[1][1]=9
array[2][0]=10 array[2][1]=13
而在 Fortran 中,输出是
arr( 1 , 1 ) = 1.7994937190948764E-305
arr( 1 , 2 ) = 7.1027035167764720E-251
arr( 1 , 3 ) = 9.8813129168249309E-324
arr( 2 , 1 ) = 5.4809152658772852E-317
arr( 2 , 2 ) = 1.5475240269406953E-314
arr( 2 , 3 ) = 0.0000000000000000
所以不知何故,值没有正确传递。
主要原因是您的 C 数组是一个锯齿状数组,它是一个指向单独一维数组的指针数组,而在 Fortran 中您将参数声明为连续的二维数组。您必须在两个部分中使用相同的内容,最好在 C 中也使用连续的数组。
只需 malloc
一个大的 nx*ny
缓冲区并将指针设置为行而不是分配它们。您可以在
我最近在处理需要在 Fotran 中保持数组形状(不能只是一维)时使用 C_F_POINTER 取得了一些成功:
由于在从 C++ 传递动态分配的数组时传递指向值指针数组的指针(对于 2D),您可以对每个 c_ptr 使用 C_F_POINTER() 方法来帮助您在 Fortran 端填写数组。
在您的 Fortran 子例程中,您将包含数组的变量设置为行的一维数组。这包含一个指针数组,每个指针指向一维指针数组,这些指针将用您的列填充您的行。在 Fortran 方面,您需要一个 Fortran 指针的一维数组和一个将成为您的实际值的二维数组。
C++:
double** arrc = new double*[x]
for (i=0;i<x;i++)
arrc[i] = new double*[y]
some_fortran_call(arrc)
Fortran:
subroutine some_fortran_call(arrc) bind(c, name='some_fortran_call')
use iso_c_binding
type(c_ptr), intent(inout):: arrc(x)
real*8, pointer:: arrp(:)
real*8, dimension(:,:), allocatable:: arrf
allocate(arrf(x,y))
do i=1,x
call C_F_POINTER(arrc(i), arrp, [y]) ----> Each pointer in arrc points to a 1D array with y values
do j=1,y
arrf(i,j) = arrp(j) ---------> Fills in ith row with y amount of values (will do this for all x rows)
end do
end do
您仍然需要为 Fortran 数组分配新内存,但这至少节省了在 C++ 中分配新一维数组所需的内存。