我正在使用一些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代码是以自由格式编写的,并使用英特尔编译器
感谢您的帮助!
答案 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)