Fortran:指针数组的数组?

发布于 2024-11-29 05:14:26 字数 682 浏览 0 评论 0原文

我正在使用一些 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 代码以自由形式编写并使用英特尔编译器

感谢您的帮助!

I'm working with some Fortran code (which I'd never used until this project...) and have come up against a problem. I need to share some memory space with another program. To get Fortran to recognise each memory chunk I use the following code:

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

Where:

VarLoc(i) is an integer representing the memory location

VarNames(i) ?Array of pointer arrays?

The problem I'm having is creating the VarNames array of pointer arrays. I've found some sample code from my googling but I find Fortran quite difficult to understand!! Can anyone show me how to set up an array of pointer arrays? Or point out an alternative if I'm approaching the problem incorrectly?

For reference the Fortran code is written in free form and uses an intel compiler

Thanks for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

红衣飘飘貌似仙 2024-12-06 05:14:26

好的,所以我假设冒号与连续行有关并忽略它们,并且您正在尝试执行此操作:

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)将其转换为形状 [3] 的 Fortran 指针。

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

所以你希望 VarNames_ 成为一个由 5 个指针组成的数组,指向由 3.. 某些东西组成的数组,你没有说。比如说整数。

让我们从简单的情况来看:假设我们在 C 中有一个一维整数数组,并且想要在 Fortran 中有一个指向它们的指针。如果我们的 C 例程是这样的 (crooutine.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 
lt;

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 不容易让您定义指针数组;您必须创建一个已定义的类型(Fortran 相当于 C 中的结构)。但这很容易。让我们按照我推荐的方式做事,在 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)

Ok, so I'm going to assume the colons have something to do with continuation lines and ignore them, and that you're trying to do this:

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

What this is doing is: (the TRANSFER) taking an integer representing existing C pointer (I assume) memory_location, add an offset to that (VarNamesLoc_(i)), and turn it into a type of c_ptr. And then (the C_F_POINTER) converting that to a Fortran pointer of shape [3].

I don't think doing the C-pointers arithmetic on the Fortran side is a great idea, but.

So you want VarNames_ to be an array of 5 pointers to arrays of 3.. somethings, you haven't said. Let's say integers.

Let's take it from the simple case: let's just say we had a 1 dimensional array of ints in C, and wanted a pointer to them in Fortran. If our C routine was this (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;
}

our Fortran driver might look like this (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

and a Makefile like this:

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

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

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

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

and building it and running it we get the expected answer:

$ ./main 
 fdata =            0           1           2           3           4

Note that in C we allocated an array of 5 ints; the Fortran program is mostly defining the interface to the C routines so we can call them from Fortran, then calling them. The c_f_pointer does the translation between the c pointer (cdata) and the Fortran pointer (fdata).

If we wanted to do some C-pointer arithmetic in Fortran, we could do this:

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)

But I really wouldn't recommend this; better to do the pointer manipulations in 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)

Cleaner, less likely to cause wierd errors, and smaller, too!

Ok, so finally, let's do an array of pointers. This, admittedly, is harder than it should be in Fortran because Fortran doesn't easily let you define arrays of pointers; you have to create a defined type (the Fortran equivalent of a struct in C). But it's easy enough. Let's do things the way I recommend, doing the pointer math on the Fortran side:

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)

Here we've created a type ptrelelment which is a 1-d pointer array, then created an array of those. That gives us our array of pointers, which we set by taking slices of fdata, which is still the pointer to the whole data.

running gives us

$ ./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

Or, as I do not suggest, doing the C-style pointer math in Fortran:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文