Fortran:指针数组的数组?

时间:2011-08-09 15:47:06

标签: arrays fortran

我正在使用一些Fortran代码(在此项目之前我从未使用过这个代码......)并且遇到了一个问题。我需要与另一个程序共享一些内存空间。为了让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数组。我从谷歌搜索中找到了一些示例代码,但我发现Fortran很难理解!!任何人都可以告诉我如何设置指针数组的数组?或者如果我不正确地接近问题,请指出替代方案?

作为参考,Fortran代码是以自由格式编写的,并使用英特尔编译器

感谢您的帮助!

1 个答案:

答案 0 :(得分:8)

好的,所以我会假设冒号与延续线有关并忽略它们,而你正试图这样做:

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

这是做什么的:(TRANSFER)取一个表示现有C指针的整数(我假设)memory_location,向它添加一个偏移量(VarNamesLoc_(i)),并将其转换为一种类型c_ptr。然后(C_F_POINTER)将其转换为形状[3]的Fortran指针。

我不认为在Fortran端做C指针算法是一个好主意,但是。

所以你希望VarNames_成为一个包含3个数组的5个指针的数组...你还没有说。我们假设整数。

让我们从简单的案例中得出结论:让我们说我们在C中有一个1维的int数组,并希望在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)之间的转换。

如果我们想在Fortran中做一些C指针算术,我们可以这样做:

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)

但我真的不推荐这个;最好在Fortran中进行指针操作:

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中应该更难,因为Fortran不容易让你定义指针数组;你必须创建一个已定义的类型(在C中的Fortran等效结构)。但这很容易。让我们按照我推荐的方式做事,在Fortran端进行指针数学运算:

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,它是一个指针数组,然后创建了一个数组。这给了我们指针数组,我们通过获取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

或者,正如我建议的那样,在Fortran中进行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)