正确地将多维C数组传递给fortran,使得size函数(在fortran中)在每个维度中获得正确的大小?

时间:2022-07-05 21:36:25

I have a fortran subroutine like this which expects a 3d array and prints the size along each dimension:

我有一个像这样的fortran子程序,它需要一个3d数组并沿每个维度打印大小:

subroutine printarray(arr)
real*8 :: arr(:,:,:)

print*, "Dimensions of arr -> ", size(arr(:,1,1)), size(arr(1,:,1)), size(arr(1,1,:))

end subroutine printarray

Now I want to pass a 3d array from a C program to this subroutine.

现在我想将一个3D数组从C程序传递给这个子程序。

I attempted it in many ways, none of them worked.

我尝试过很多方面,但都没有奏效。

Attempt 1:

尝试1:

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

void printarray_(double *);

int main(int argc, char *argv[])
{
  double *ap;

  ap = (double *) malloc(sizeof(double) * 10 * 10 * 2);
  printarray_(ap);

  if(ap != NULL)
    free(ap);
  return 0;
}

Output is : 1 1 1

输出为:1 1 1

One issue here is I haven't explicitly specified the size of each dimension in my C code itself, so I cannot expect Fortran to figure it out. So I tried it differently:

这里的一个问题是我没有明确指定我的C代码本身中每个维度的大小,所以我不能指望Fortran能够弄明白。所以我尝试了不同的方式:

Attempt 2:

尝试2:

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

void printarray_(double ***);

int main(int argc, char *argv[])
{
  int i;
  int j;
  double ***a;
  a = (double ***) malloc(sizeof(double **) * 200);
  for(i = 0;i < 10;i++)
    {
      a[i] = (double **) malloc(sizeof(double *) * 10);
      for(j = 0;j < 10;j++)
      {
        a[i][j] = (double *) malloc(sizeof(double) * 2);
      }
    }

  printarray_(a);

  for(i = 0; i < 10;i++)
    {
      for(j = 0;j < 10;j++)
      {
        if(a[i][j] != NULL)
          free(a[i][j]);
      }
      if(a[i] != NULL)
        free(a[i]);
    }
  if(a != NULL)
    free(a);
  return 0;
}

Here, the output is : 289 289 1

这里的输出是:289 289 1

What is the correct way of passing multidimensional arrays to Fortran subroutines from C programs, such that the size function in fortran picks up correct size of each dimension?

将多维数组从C程序传递给Fortran子程序的正确方法是什么,以便fortran中的size函数能够获取每个维度的正确大小?

2 个解决方案

#1


1  

You just can not do it in C, because arrays in C decay to pointers during a function call.

你不能在C中做到这一点,因为C中的数组在函数调用期间衰减为指针。

There are a few ways to do it: Function parameter as array with declared size

有几种方法可以做到:函数参数为声明大小的数组

But best and most common practice is still to pass in dimensions as separate inputs, and also pass in the address of of the beginning element and let subroutine figure out the rest itself.

但最好和最常见的做法仍然是将维度作为单独的输入传递,并且还传入开始元素的地址,让子程序找出其余部分。

And your second attempt is dangerous because dimensions are not contiguous but your function might expect/assume it to be so.

而你的第二次尝试是危险的,因为尺寸不是连续的,但你的功能可能会预期/假设它是如此。

#2


1  

If your Fortran compiler supports ISO/IEC TS 29113:2012 , you can use the ISO_Fortran_binding.h header for interoperability of Fortran arrays with C. I am not sure which compilers support the TS; unfortunately, gfortran does not do so, in this respect. So, assumed-shape arrays like arr(:,:,:) might not be an option for you.

如果您的Fortran编译器支持ISO / IEC TS 29113:2012,则可以使用ISO_Fortran_binding.h头来实现Fortran阵列与C的互操作性。我不确定哪些编译器支持TS;不幸的是,在这方面,gfortran并没有这样做。因此,像arr(:,:,:)的假设形状数组可能不适合您。

Failing that, you should use Fortran's Iso C binding feature.

如果做不到这一点,你应该使用Fortran的Iso C绑定功能。

Note that Fortran arrays consist of consecutive storage locations. For 1D-arrays, this is not an issue for C interop. For arrays with two or more dimensions, this creates problems because the normal C way of accessing arrays through pointers to pointers to ... has no good equivalent in Fortran (and is inefficient anyway).

请注意,Fortran阵列由连续的存储位置组成。对于1D阵列,这不是C interop的问题。对于具有两个或更多维度的数组,这会产生问题,因为通过指向指向...的指针访问数组的正常C方式在Fortran中没有很好的等价物(并且无论如何都是低效的)。

The closest you can get in C to Fortran arrays is through the use of variable-length arrays. Unfortunately, these have been relegated to optional in the last version of the C standard, so C support for multidimensional arrays has fallen back behind Fortran 66 again. However, I am assuming that you have a compiler which supports VLAs (gcc does) and you do not hit the limitations that sometimes plague the implementaions that, for example, VLAs are only allocated on the stack and that the stack is too small for your problem size...

在C到Fortran数组中最接近的是通过使用可变长度数组。不幸的是,这些已经在C标准的最后一个版本中被降级为可选项,因此对多维数组的C支持再次落后于Fortran 66。但是,我假设您有一个支持VLA(gcc)的编译器,并且您没有遇到有时会困扰实现的限制,例如,VLA仅在堆栈上分配,并且堆栈对于您来说太小问题规模......

So, you should be able to declare

所以,你应该能够宣布

  int m,n,k;
  // Obtain a value for m,n,k
  {
     double arr[10][10][2];
     // Do something with it
     printarray(a,k,m,n)  // Fortran arrays have the opposite ordering of indices versus C
  }

... and have on the Fortran side

......并且在Fortran一侧

subroutine printarray(arr,k,m,n) bind(C,name="printarray")
  use iso_c_binding
  integer(c_int), value :: k, m, n
  real(c_double), dimension(k,m,n) :: arr

#1


1  

You just can not do it in C, because arrays in C decay to pointers during a function call.

你不能在C中做到这一点,因为C中的数组在函数调用期间衰减为指针。

There are a few ways to do it: Function parameter as array with declared size

有几种方法可以做到:函数参数为声明大小的数组

But best and most common practice is still to pass in dimensions as separate inputs, and also pass in the address of of the beginning element and let subroutine figure out the rest itself.

但最好和最常见的做法仍然是将维度作为单独的输入传递,并且还传入开始元素的地址,让子程序找出其余部分。

And your second attempt is dangerous because dimensions are not contiguous but your function might expect/assume it to be so.

而你的第二次尝试是危险的,因为尺寸不是连续的,但你的功能可能会预期/假设它是如此。

#2


1  

If your Fortran compiler supports ISO/IEC TS 29113:2012 , you can use the ISO_Fortran_binding.h header for interoperability of Fortran arrays with C. I am not sure which compilers support the TS; unfortunately, gfortran does not do so, in this respect. So, assumed-shape arrays like arr(:,:,:) might not be an option for you.

如果您的Fortran编译器支持ISO / IEC TS 29113:2012,则可以使用ISO_Fortran_binding.h头来实现Fortran阵列与C的互操作性。我不确定哪些编译器支持TS;不幸的是,在这方面,gfortran并没有这样做。因此,像arr(:,:,:)的假设形状数组可能不适合您。

Failing that, you should use Fortran's Iso C binding feature.

如果做不到这一点,你应该使用Fortran的Iso C绑定功能。

Note that Fortran arrays consist of consecutive storage locations. For 1D-arrays, this is not an issue for C interop. For arrays with two or more dimensions, this creates problems because the normal C way of accessing arrays through pointers to pointers to ... has no good equivalent in Fortran (and is inefficient anyway).

请注意,Fortran阵列由连续的存储位置组成。对于1D阵列,这不是C interop的问题。对于具有两个或更多维度的数组,这会产生问题,因为通过指向指向...的指针访问数组的正常C方式在Fortran中没有很好的等价物(并且无论如何都是低效的)。

The closest you can get in C to Fortran arrays is through the use of variable-length arrays. Unfortunately, these have been relegated to optional in the last version of the C standard, so C support for multidimensional arrays has fallen back behind Fortran 66 again. However, I am assuming that you have a compiler which supports VLAs (gcc does) and you do not hit the limitations that sometimes plague the implementaions that, for example, VLAs are only allocated on the stack and that the stack is too small for your problem size...

在C到Fortran数组中最接近的是通过使用可变长度数组。不幸的是,这些已经在C标准的最后一个版本中被降级为可选项,因此对多维数组的C支持再次落后于Fortran 66。但是,我假设您有一个支持VLA(gcc)的编译器,并且您没有遇到有时会困扰实现的限制,例如,VLA仅在堆栈上分配,并且堆栈对于您来说太小问题规模......

So, you should be able to declare

所以,你应该能够宣布

  int m,n,k;
  // Obtain a value for m,n,k
  {
     double arr[10][10][2];
     // Do something with it
     printarray(a,k,m,n)  // Fortran arrays have the opposite ordering of indices versus C
  }

... and have on the Fortran side

......并且在Fortran一侧

subroutine printarray(arr,k,m,n) bind(C,name="printarray")
  use iso_c_binding
  integer(c_int), value :: k, m, n
  real(c_double), dimension(k,m,n) :: arr