1

Я понимаю, что extra care needs to be taken при распределении памяти в C, чтобы обеспечить массив 2d, но я все еще не получаю ожидаемых результатов, когда передаю его Fortran. Ниже приведена игрушечная версия моей попытки: файл main.c, который выделяет память для 2d-массива и присваивает значение каждому элементу, и файл foo.f90, который выводит элементы массива 2d.Попытка передать непрерывный динамический массив 2d от C до Fortran

#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

Существует ** нет ** 2D-массив в вашем коде C и ничего, что может представлять или указывать на один! Указатель не является массивом. Если вам нужен 2D-массив, используйте его! – Olaf

+1

Двойной указатель может представлять собой 2D-массив и на самом деле есть @Olaf, я думаю, проблема в foo_ (double **, int *, int *); не должно быть просто foo_ (double **, int, int); ? – koper89

+0

@Olaf Ваш комментарий либо неправильный, либо слишком тупой, чтобы быть дидактически бесполезным. На языке программирования C многомерные массивы, хранящиеся в куче, сохраняют память во время выполнения, а указатели используются в качестве инструментов ведения бухгалтерского учета для управления адресами (https://www.cs.swarthmore.edu/~newhall/unixhelp/C_arrays.html). – DJames

ответ

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), чтобы соответствовать int на стороне C, например что

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).


[EDIT]

Используя современные 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 для предложений).

+0

Возможно, это верно. Fortran ABI использует 1D массивы для взаимодействия с кодом C, но это должно быть проверено первым. В конце концов, это все еще очень отличается от того, что использует неровный массив OP, а не 2D-массив. Поскольку он имеет то же самое (с учетом правильного порядка строк/col order), он должен работать, но все же он имеет другой тип и может привести к тонким ошибкам, соответственно. осложнения и требуют иначе ненужных бросков .. – Olaf

+1

@Olaf Массив OP создал не зубчатый, я думаю ... – roygvib

+0

OP использует указатель на указатель, соответственно. массив указателей. Обычно это называется «зубчатым массивом», независимо от массивов второго уровня, такого же размера или нет. Тот факт, что он выделяет блок sub-arrays en, не меняет этого. Правильный способ в C-only - использовать 2D-массив, но, как я писал, я не уверен, что ожидает Fortran. – Olaf

1

Вы сами так сказали: смежный!

Вы не выделяете непрерывный массив. Для того, чтобы выделить тот, который вы должны написать:

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

К сожалению, теперь вы должны написать в C индексировать 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; 

Это предполагает матрицу по строкам крупного заказа (строка за строкой после ряд, расположенный в памяти).

Примечание: на C99 имеются переменные длины массивов, где компилятор правильно управляет x[i][j]. У меня нет C99, но, возможно, другой пользователь может дать ответ с VLA.

+0

Это ошибка типа. 'x' является' double ** ', и ваш индекс неверен. И почему бы не использовать 2D-массив? Все зависит от того, что ожидает Fortran ABI. – Olaf

+0

@ Олаф, спасибо, что заметили его. Починил это. –

+0

Вопрос помечен C, который будет стандартным C, который является C11. В то время как C11 делает VLA опциональным, ни один современный компилятор C11-совместимого не поддерживает их (создание необязательной языковой функции - это серьезный разрыв с практикой коммитов по поддержанию совместимости вниз, вероятно, для тех, кто поддерживает устаревшие компиляторы, чтобы лучше их продавать). – Olaf

Смежные вопросы