我从Fortran 90程序调用C函数(我必须使用Fortran 90 )。这个C函数接受一些参数并返回一个浮点指针。我似乎无法在Fortran代码中正确打印返回的数据。它只显示一个非常大的数字(我假设它是指针的地址。)
我成功地将REAL Fortran变量作为参数传递,让C函数设置它们(当Fortran通过引用传递)并然后访问数据。但是,我必须将指针作为返回变量返回,因为这是遗留函数使用的方法(我正在重新实现它。)
Fortran 90中是否有办法从C函数返回的非字符(real,int等)指针访问数据? (请注意:我不能使用ISO C绑定,因为这仅适用于Fortran 2003及更高版本。)我已经了解了我在下面尝试做的事情......
谢谢!
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
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
答案 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指针的资源: