无法从Fortran 90中返回的C浮点指针获取数据

时间:2013-03-22 20:05:04

标签: c pointers fortran fortran90 fortran-iso-c-binding

我从Fortran 90程序调用C函数(我必须使用Fortran 90 )。这个C函数接受一些参数并返回一个浮点指针。我似乎无法在Fortran代码中正确打印返回的数据。它只显示一个非常大的数字(我假设它是指针的地址。)

我成功地将REAL Fortran变量作为参数传递,让C函数设置它们(当Fortran通过引用传递)并然后访问数据。但是,我必须将指针作为返回变量返回,因为这是遗留函数使用的方法(我正在重新实现它。)

Fortran 90中是否有办法从C函数返回的非字符(real,int等)指针访问数据? (请注意:我不能使用ISO C绑定,因为这仅适用于Fortran 2003及更高版本。)我已经了解了我在下面尝试做的事情......

谢谢!

Fortran计划

program test_real
    real, dimension(10) :: realpt
    integer nbr
    integer i
    real :: a=1.0
    real :: b=2.0
    real :: c=3.0

    nbr = 9
    realpt = getpointer(a, b, c)

    do 10 i = 1, nbr
        print *,"return: ",realpt(i)
10  continue

stop
End

C函数

float* getpointer(float *a, float *b, float *c) {
    float *rfl = (float *) calloc(9,sizeof(float));
    int i=0;

    for(i=0;i<3;i++) {
        rfl[i] = *a;
    }
    for(i=3;i<6;i++) {
        rfl[i] = *b;
    }
    for(i=6;i<9;i++) {
        rfl[i] = *c;
    }
    return(rfl);
}   // End of getpointer function

输出

return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07

3 个答案:

答案 0 :(得分:4)

此声明“我不能使用ISO C绑定,因为它仅适用于Fortran 2003及更高版本。”很奇怪;任何支持F90的当前编译器也支持大部分或全部F2003。

正如Eric Urban指出的那样,最简单的方法就是在fortran中进行数组分配(实际上你可以使用数组切片或广播来更容易地进行填充)。但是假设有一些C例程你需要调用它采用这种形式,只需使用ISO_C_BINDING模块进行C和fortran之间的可移植接口:

program test_real
    use, intrinsic :: iso_c_binding
    real(kind=c_float), pointer :: realpt(:)
    type(c_ptr) :: ptr
    integer :: nbr
    integer :: i
    real :: a=1.0
    real :: b=2.0
    real :: c=3.0

    interface
       function getpointer(a, b, c) result(ptr) bind(C,name="getpointer")
           use, intrinsic :: iso_c_binding
           implicit none
           type(c_ptr) :: ptr
           real(kind=c_float) :: a, b, c
        end function getpointer
      end interface

    nbr = 9
    ptr = getpointer(a, b, c)
    call c_f_pointer(ptr, realpt, [nbr])

    print *,"return: ",realpt
end

编译并运行

$ gcc -c fooc.c
$ gfortran -c foo.f90
$ gfortran -o foo foo.o fooc.o
$ ./foo
 return:    1.0000000       1.0000000       1.0000000       2.0000000       2.0000000       2.0000000       3.0000000       3.0000000       3.0000000

如果你试图以某种方式做到这一点,以解决7年以上的编译器限制,那么有一些脆弱的非便携方式可以做到这一点,但它真的不值得心痛。

更新:如果您无法触摸(甚至重新编译)某些较旧的fortran,那么您至少可以为C程序制作一个F2003包装器,并将旧的fortran链接到它:

foowrapper.f90:

module wrapper

contains

subroutine wrapgetpointer(outarr)
    use, intrinsic :: iso_c_binding
    implicit none

    real, intent(out), dimension(:) :: outarr
    real(kind=c_float), pointer :: realpt(:)
    type(c_ptr) :: ptr
    integer :: nbr = 9
    real(kind=c_float) :: a, b, c

    interface
       function getpointer(a, b, c) result(ptr) bind(C,name="getpointer")
           use, intrinsic :: iso_c_binding
           implicit none
           type(c_ptr) :: ptr
           real(kind=c_float) :: a, b, c
        end function getpointer
      end interface

    a = 1.
    b = 2.
    c = 3.
    ptr = getpointer(a, b, c)
    call c_f_pointer(ptr, realpt, [nbr])

    outarr(1:nbr) = realpt
end subroutine wrapgetpointer

end module wrapper

foo.f90:

program test_real
    use wrapper

    real, dimension(9) :: array
    call wrapgetpointer(array)

    print *,"return: ",array
end

编译并运行:

$ gfortran -c foowrapper.f90
$ gfortran -c foo.f90
$ gcc -c fooc.c
$ gfortran -o foo foo.o foowrapper.o fooc.o
$ ./foo
 return:    1.0000000       1.0000000       1.0000000       2.0000000       2.0000000       2.0000000       3.0000000       3.0000000       3.0000000

答案 1 :(得分:1)

在您的代码中隐含无,您就会明白为什么没有得到您想要的结果。

Fortran中的数组值函数通常通过传递“隐藏”参数来实现,该参数是函数应写入其结果的内存中的地址。这与您的用例没有多大的共同之处。

如果坚持使用Fortran 90,则无法执行此操作。您需要使用某种扩展名。

请注意,F2003的C互操作性是F90的扩展...

(Cray指针也是,但如果你必须使用扩展名,我知道我选择哪一个。)

答案 2 :(得分:0)

我最终没有实现包装器解决方案。对于不能使用Fortran 2003的情况,似乎对我有用的是Cray指针。我不确定这些在新编译器中的可行性是什么,但对于我正在使用的F90编译器(Intel ifort 9.1),它运行良好。

Cray指针可以与返回的C指针一起使用。为了将Fortran中的Cray指针声明为float数组,例如:

real, dimension(10) :: realpt
pointer (ptemp,realpt) 
nbr = 9

然后你可以像这样调用C函数:

    ptemp = getpointer(a, b, c)    
    do 10 i = 1, nbr        
        print *,"return: ",realpt(i)
10  continue

这是一个解释如何使用Cray指针的资源:

http://gcc.gnu.org/onlinedocs/gfortran/Cray-pointers.html