C_F_POINTER приводит к неопределенному массиву

Я попытался изменить код из этого вопроса на ISO_C_BINDING , но вызов C_F_POINTER приводит к неопределенному указателю/массиву (отладчик VS2010), а print*, f_arr(i) вызывает ошибку сегментации. Думаю, я внимательно следую инструкциям из взаимодействие с массивом.

main.c

extern void subr(int, float*);
int main(int argc, char **argv){
    int N = 3;
    int i;
    float data[3];
    for (i=0;i<N;i++) data[i]=i;
    subr(N,data);
}

саб.f90

subroutine subr(n, c_arr) BIND(C)
USE, INTRINSIC :: ISO_C_BINDING 
implicit none

INTEGER(C_INT),VALUE :: n
TYPE (C_PTR) :: c_arr
REAL(C_FLOAT), POINTER :: f_arr (:)
integer :: i,ml,mu

CALL C_F_POINTER (c_arr, f_arr, (/n/) )
ml = lbound(f_arr,1)
mu = ubound(f_arr,1)

do i=ml,mu
  print*, f_arr(i)
enddo
end subroutine

Вы знаете, почему я получаю ошибку при преобразовании указателя? (компилятор Intel Fortran)


person Peter    schedule 28.01.2014    source источник


Ответы (1)


Пример вызова Fortran из C через ISO_C_BINDING в документации Intel не очень ясен. Но в другом месте я нашел это объяснение: TYPE (C_PTR) :: X эквивалентно void ** C, т.е. указатель на пустой указатель. Нам нужен void *, пустой указатель, и поэтому мы должны объявить C_ARR с атрибутом VALUE:

SUBROUTINE SUBR(N, C_ARR) BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING 
    IMPLICIT NONE

    INTEGER (C_INT), VALUE :: N
    TYPE (C_PTR), VALUE :: C_ARR
    REAL (C_FLOAT), POINTER :: F_ARR(:)

    CALL C_F_POINTER(C_ARR, F_ARR, (/N/))                                     

    PRINT *, F_ARR

END SUBROUTINE

Но я не уверен, что вам вообще нужно звонить C_F_POINTER. Также работает следующее:

SUBROUTINE SUBR(N, ARR) BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING 
    IMPLICIT NONE

    INTEGER (C_INT), VALUE :: N
    REAL (C_FLOAT) :: ARR(N)

    PRINT *, ARR

END SUBROUTINE

(Возможно, использование C_F_POINTER является хорошей практикой, я не знаю. Документация Intel должна показывать это: когда это необходимо, а когда нет.)

Редактировать: после более подробного изучения интерфейса в примере с Intel, я думаю, что здесь необходим вызов C_F_POINTER, потому что массив является частью C struct. При объявлении вашего TYPE вы не можете создавать массивы элементов переменной длины, поэтому вам нужно объявить элемент данных как TYPE (C_PTR) и пройти процедуру C_F_POINTER.

Итак, вот тот же пример передачи указателя на очищенную структуру:

SUBROUTINE SUBR_STRUCT(OBJ) BIND(C)
    USE, INTRINSIC :: ISO_C_BINDING 
    IMPLICIT NONE

    TYPE, BIND(C) :: VECTOR 
        INTEGER (C_INT) :: LEN
        TYPE (C_PTR) :: DATA
    END TYPE VECTOR

    TYPE (VECTOR), INTENT(IN) :: OBJ
    REAL (C_FLOAT), POINTER :: ARR(:)

    CALL C_F_POINTER (OBJ%DATA, ARR, (/OBJ%LEN/))

    PRINT *, ARR

END SUBROUTINE

Это вызывается из C таким образом:

struct obj
{
    int len;
    float *data;
};

extern void subr_struct(const struct obj *);

int main(int argc, char **argv)
{
    float data[] = {0.12, 0.15, 0.18, 0.23, 0.29};
    struct obj o = {5, data};

    subr_struct(&o);

    return 0;
}

Вы также можете передать структуру по значению, если вы дадите OBJ атрибут VALUE.

person M Oehm    schedule 28.01.2014
comment
Вы правы, вообще не нужно играть со значением указателя, а более короткая версия лучше (и действительна). type(c_ptr) без value на самом деле void ** - person Vladimir F; 28.01.2014
comment
Верно, но я пытался использовать нотацию Arr(:) - person Peter; 28.01.2014
comment
@VladimirF: Да, конечно, void **. Глупая опечатка, компилятор бы ее уловил. Спасибо. - person M Oehm; 28.01.2014
comment
@Peter Вы не можете использовать (:) отложенную или предполагаемую форму для аргументов процедуры bind(C). Это против стандарта. - person Vladimir F; 28.01.2014