Фортран: Массив массивов указателей? - PullRequest
2 голосов
/ 09 августа 2011

Я работаю с некоторым кодом на Фортране (который я никогда не использовал до этого проекта ...) и столкнулся с проблемой. Мне нужно разделить некоторое пространство памяти с другой программой. Чтобы заставить Fortran распознавать каждый фрагмент памяти, я использую следующий код:

       do 10 i = 0, 5 
       CALL C_F_POINTER(TRANSFER(memory_location +
       : VarNamesLoc_(i), 
       : memory_location_cptr) , VarNames_(i), [3]) 
       exit 
    10 continue

Где:

VarLoc (i) - целое число, представляющее область памяти

VarNames (i)? Массив массивов указателей?

У меня проблема с созданием массива массивов указателей VarNames. Я нашел некоторый пример кода из моего поиска в Google, но я считаю, что Fortran довольно сложно понять !! Может кто-нибудь показать мне, как настроить массив массивов указателей? Или укажите альтернативу, если я неправильно подхожу к проблеме?

Для справки код Фортрана написан в свободной форме и использует компилятор Intel

Спасибо за любую помощь!

1 Ответ

8 голосов
/ 10 августа 2011

Хорошо, я собираюсь предположить, что двоеточия имеют какое-то отношение к строкам продолжения, и игнорировать их, и вы пытаетесь это сделать:

do i = 0, 5 
    CALL C_F_POINTER(&
          TRANSFER(memory_location + VarNamesLoc_(i), memory_location_cptr), &
          VarNames_(i), [3] ) 
enddo 

То, что он делает, это: (ПЕРЕДАЧА) берет целое число, представляющее существующий указатель C (я предполагаю) memory_location, добавляем к нему смещение (VarNamesLoc_(i)) и превращаем его в тип c_ptr. И затем (C_F_POINTER) преобразует это в указатель формы Fortran [3].

Я не думаю, что делать арифметику С-указателей на стороне Фортрана - отличная идея, но.

Итак, вы хотите, чтобы VarNames_ был массивом из 5 указателей на массивы из 3 .. чего-то, что вы не сказали. Скажем, целые числа.

Давайте возьмем это из простого случая: скажем, у нас был одномерный массив целых чисел в C, и мы хотели указатель на них в Fortran. Если бы наша подпрограмма C была такой (croutine.c):

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

void makearray(int **data, int n) {
    *data = (int *)malloc(n * sizeof(int));
    for (int i=0; i<n; i++) 
        (*data)[i] = i;
    return;
}

void freearray(int **data, int n) {
    free(*data);
    return;
}

наш драйвер Fortran может выглядеть следующим образом (driver.f90):

PROGRAM interoptesting
    USE, intrinsic :: iso_c_binding
    USE, intrinsic :: iso_fortran_env
    IMPLICIT NONE

    INTERFACE
        !! C prototype: void makearray(int **data, int n)
        SUBROUTINE makearray(data, n) BIND(C)
            USE, intrinsic :: iso_c_binding
            type(c_ptr)    :: data
            integer(kind=c_int), value :: n
        END SUBROUTINE makearray
        !! C prototype: void freearray(int **data, int n)
        SUBROUTINE freearray(data, n) BIND(C)
            USE, intrinsic :: iso_c_binding
            type(c_ptr)    :: data
            integer(kind=c_int), value :: n
        END SUBROUTINE freearray
    END INTERFACE 

    type(c_ptr) :: cdata
    integer, pointer, dimension(:) :: fdata
    integer                        :: n = 5

    call makearray(cdata, n);
    call c_f_pointer(cdata, fdata, [n])
    print *, 'fdata = ', fdata
    call freearray(cdata, n)

END program

и Makefile вроде этого:

FC=gfortran
CC=gcc
CFLAGS=-std=c99 -g
FFLAGS=-g

main: driver.o croutine.o 
        $(FC) -o $@ $^

driver.o: driver.f90
        $(FC) $(FFLAGS) -c $<

clean:
        rm -rf main driver.o croutine.o 

и, собрав его и запустив, мы получим ожидаемый ответ:

$ ./main 
 fdata =            0           1           2           3           4

Обратите внимание, что в C мы выделили массив из 5-ти дюймов; Программа Fortran в основном определяет интерфейс для подпрограмм C, поэтому мы можем вызывать их из Fortran, а затем вызывать их. c_f_pointer выполняет перевод между указателем c (cdata) и указателем Fortran (fdata).

Если мы хотим сделать некоторую арифметику с C-указателем в Fortran, мы можем сделать это:

type(c_ptr) :: cdata, newdata
integer(kind=int64)            :: tmpint
integer, pointer, dimension(:) :: fdata
integer                        :: n = 5
integer(kind=c_int) :: cint

call makearray(cdata, n);

! copy pointer to an int
tmpint = TRANSFER(cdata, tmpint)
! add two integer sizes:
tmpint = tmpint + 2*c_sizeof(cint)
! copy back into a pointer
newdata= TRANSFER(tmpint, newdata)

call c_f_pointer(newdata, fdata, [n-2])
print *, 'fdata = ', fdata
call freearray(cdata, n)

Но я действительно не рекомендовал бы это; лучше сделать манипуляции с указателем в Фортране:

type(c_ptr) :: cdata
integer, pointer, dimension(:) :: fdata, newfdata
integer                        :: n = 5

call makearray(cdata, n);
call c_f_pointer(cdata, fdata, [n])

newfdata => fdata(3:n)
print *, 'newfdata = ', newfdata
call freearray(cdata, n)

Более чистый, менее вероятный, чтобы вызвать странные ошибки, и меньший, также!

Хорошо, наконец, давайте сделаем массив указателей. По общему признанию, это сложнее, чем должно быть в Фортране, потому что Фортран нелегко позволяет вам определять массивы указателей; Вы должны создать определенный тип (Fortran-эквивалент структуры в C). Но это достаточно просто. Давайте сделаем то, что я рекомендую, выполняя указатель по математике на стороне Фортрана:

type(c_ptr) :: cdata
integer, pointer, dimension(:) :: fdata
integer                        :: n = 10
integer, parameter             :: nptrs = 5
integer :: i, intsperptr, istart, iend

! our new "ptrelement" type which we can define arrays of
type ptrelement
     integer, pointer, dimension(:) :: p
end type ptrelement
type(ptrelement) :: ptrs(nptrs)

call makearray(cdata, n);
call c_f_pointer(cdata, fdata, [n])

intsperptr = n/nptrs
do i=1,nptrs
    istart = (i-1)*intsperptr+1
    iend   = istart + intsperptr-1
    ptrs(i)%p => fdata(istart:iend)
enddo

do i=1,nptrs
    print '(A,I2,A,99(I5,X))', 'ptrs(',i,')%p = ', ptrs(i)%p
enddo

call freearray(cdata, n)

Здесь мы создали тип ptrelelment, который является массивом 1-d указателей, а затем создали их массив. Это дает нам наш массив указателей, который мы устанавливаем, взяв кусочки fdata, которые по-прежнему являются указателем на все данные.

бег дает нам

$ ./main
ptrs( 1)%p =     0     1
ptrs( 2)%p =     2     3
ptrs( 3)%p =     4     5
ptrs( 4)%p =     6     7
ptrs( 5)%p =     8     9

Или, как я не предлагаю, сделать математику указателя в стиле C на Фортране:

type(c_ptr) :: cdata
integer                        :: n = 10
integer, parameter             :: nptrs = 5
integer :: i, intsperptr
integer(kind=c_int) :: cint
integer(kind=int64) :: cdata_as_int

! our new "ptrelement" type which we can define arrays of
type ptrelement
     integer, pointer, dimension(:) :: p
end type ptrelement
type(ptrelement) :: ptrs(nptrs)

call makearray(cdata, n);
cdata_as_int = TRANSFER(cdata, cdata_as_int)

intsperptr = n/nptrs
do i=1,nptrs
    call c_f_pointer( &
         TRANSFER(cdata_as_int + (i-1)*intsperptr*c_sizeof(cint), cdata),&
         ptrs(i)%p, [intsperptr] )
enddo

do i=1,nptrs
    print '(A,I2,A,99(I5,X))', 'ptrs(',i,')%p = ', ptrs(i)%p
enddo

call freearray(cdata, n)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...