Передача динамического 2D-массива из C++ в Fortran и обратно

Передача фиксированного 2D-массива между C++ и Fortran работает нормально, однако это не так с программой, которую я написал для передачи 2D-динамического массива из C++ в Fortran.

Сторона С++

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;
}

Фортран сторона

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

Вывод в С++

 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  

Так что как-то значения не передаются правильно.


person Stam    schedule 17.02.2015    source источник
comment
Да, извините, это было частью более раннего кода - результаты, конечно, не изменятся, но я изменю это сейчас.   -  person Stam    schedule 17.02.2015
comment
Кстати, код по-прежнему противоречив в этом отношении (int против c_long), но это не вызывает этой ошибки.   -  person Vladimir F    schedule 17.02.2015
comment
Надеюсь, теперь я устранил несоответствие...   -  person Stam    schedule 17.02.2015


Ответы (1)


Основная причина в том, что ваш массив C представляет собой зубчатый массив, это массив указателей на отдельные массивы 1D, в то время как в Fortran вы объявляете аргумент как непрерывный массив 2D. Вы должны использовать одно и то же в обеих частях, предпочтительно использовать непрерывный массив и в C.

Просто malloc один большой nx*ny буфер и установите указатели на строки вместо их выделения. Вы можете увидеть пример в https://stackoverflow.com/a/5901671/721644.

person Vladimir F    schedule 17.02.2015
comment
Извините, вы предлагаете использовать double* (с размером nx*ny) вместо double**? Я не уверен, что следую вашему предложению. Вы имеете в виду что-то вроде double * array; array = (double*)malloc(nx*ny*sizeof(double)) - person Stam; 17.02.2015
comment
См. связанный пример. Вам нужен double**, но вы должны настроить его по-другому. Массив должен быть непрерывным. - person Vladimir F; 17.02.2015
comment
Я заменил все разделы инициализации следующим double *data = (double *)malloc(nx*ny*sizeof(double)); array= (double **)malloc(nx*sizeof(double*)); for (int i=0; i<nx; i++) array[i] = &(data[ny*i]);, однако я все еще получаю тот же результат в Fortran. - person Stam; 17.02.2015
comment
Да, вы должны убедиться, что передаете указатель на данные, а не указатель на указатель. Вам нужно передать data или array[0] или &(array[0][0]), а функция должна принять double *. - person Vladimir F; 17.02.2015
comment
Итак, вы говорите, что в Фортране я могу передать массив только как 1D из С++, чтобы он оставался непрерывным? - person Stam; 17.02.2015
comment
Я говорю, что вы должны передать адрес некоторого непрерывного участка памяти. Что это значит для C++, я оставляю на ваше усмотрение. Я не специалист по С++. Обратите внимание, что вы в основном используете C, а не настоящий современный C++. - person Vladimir F; 17.02.2015
comment
Меня просто удивляет, что я могу передать (частично фиксированный) 2D-массив, подобный этому extern "C" {void array2d(int[][2]); }, где 2 — произвольное число, но я не могу сделать то же самое для динамического массива. Таким образом, кажется, что единственным решением является передача его в виде одномерного массива в Фортран, который затем может реконструировать его как двумерный (используя ввод количества строк и столбцов). Было бы неплохо иметь что-то в iso_c_binding для облегчения этого! - person Stam; 17.02.2015
comment
Проблема в том, что C (на самом деле вы не используете всю мощь C++) не имеет реальных 2D-динамических массивов, а только массивы указателей. iso_c-binding с этим ничего не поделаешь, это дефицит С. - person Vladimir F; 17.02.2015
comment
Просто забыл подтвердить, что ваше предложение работает - я передаю массив как 1D, но с номерами строк и столбцов в качестве параметров, а затем реконструирую его на Фортране. Спасибо! - person Stam; 06.03.2015