求助:关于iso_c_binding中C端多重指针参数接口的写法
求教一个混编问题:
我要调用一个C语言函数:
int get_table(char ***Results );
这个函数位于一个已经封装好的动态链接库中,作用是读取一个文件,将内容存储到二维字符串数组Results中。
按照如下写法,我得到的Results是一个乱码:
接口:
interface
function get_table(Results) bind(c)
use iso_c_binding, only: c_int, c_char
character(kind=c_char),dimension(*)::Results
integer(c_int):: get_table
end function
end interface
调用:
character(8) :: Results(3,10)
i =get_table(Results)
结果:
Results(1,1) '鴴+ ' CHARACTER(8)
Results(2,1) ' ' CHARACTER(8)
Results(3,1) ' ' CHARACTER(8)
Results(1,2) ' ' CHARACTER(8)
Results(2,2) ' ' CHARACTER(8)
Results(3,2) ' ' CHARACTER(8)
Results(1,3) ' ' CHARACTER(8)
Results(2,3) ' ' CHARACTER(8)
Results(3,3) ' ' CHARACTER(8)
Results(1,4) ' ' CHARACTER(8)
……
……
这个结果并不是我预期的。
请教大神,这个interface应该如何写,Fortran中的Results参数应该如何声明?
先谢过!
本帖最后由 布衣龙共 于 2021-7-30 08:27 编辑
$ gfortran a.f90 a.c -o x
$ ./x
fcode
$
use ISO_C_Binding
implicit none
interface
integer(c_int) function get_table(val) bind(c)
import
type(c_ptr) :: val
end function
end interface
character(8) ,target :: Results(3,10)
type(c_ptr) , target :: val
integer :: i
val = c_loc(Results)
i =get_table(c_loc(val))
write(*,*) Results(1,1)
end
#include <stdio.h>
int get_table(char ***Results){
(**Results) = 'f';
(**Results) = 'c';
(**Results) = 'o';
(**Results) = 'd';
(**Results) = 'e';
} 本帖最后由 andy8496 于 2021-7-30 11:59 编辑
多谢!但是我这儿好像没成功:-(。
use ISO_C_Binding
implicit none
interface
integer(c_int) function get_table(val) bind(c)
import
type(c_ptr) :: val
end function
end interface
character(8) ,target :: Results(3,10)
type(c_ptr) , target :: val
integer :: i
val = c_loc(Results)
i =get_table(c_loc(val))
write(*,*) Results(1,1)
end
第13行中,val是不是已经是地址了,还需要套一个c_loc吗?我这么改了但是依然没成功。跟我的环境(VS+IVF)有关系吗?我再接着试试别的写法。
再接着问一个问题:
在C中,Results似乎不需要指定“大小”,这样我们不需要预先知道要读取的数据多少,就能完成读取
在Fortran中,我们一定要预先申明,character(8) ,target :: Results(3,10),也就是先知道8,3,10这些表示数据的多少的量才行吗?有没有“自动分配”的方法?
或者退而求其次,我想办法预先知道了8,3,10这些表示数据, Results能不能申明成可分配的?
如若不然,要完整的读取数据,声明Results时,大小真是不好办!
我用 windows , VS + Ifort 试了,也是OK的啊
第13行,是的, val 已经是地址了,还必须套一个 c_loc
因为 C 语言的接口是 ***Results,而不是 **Results
C 语言不是“不需要”指定大小,而是根本不知道大小(除非你额外传递给他)
所以,C 语言是很容易越界的。
int get_table(char ***Results );
这个函数,必须要求调用者分配好足够的大小。
没有自动分配的方法。
Results 可以是可分配的,但也必须分配好大小,再 get_table C函数应该是一个三维数组,你可能需要构建一个指针链给他用。
#include <stdio.h>
int get_table(char ***Results){
Results = 'f';
Results = 'c';
Results = 'o';
Results = 'd';
Results = 'e';
Results = 'f';
Results = 'c';
Results = 'o';
Results = 'd';
Results = 'e';
return 1;
}
Program Main
use ISO_C_Binding
use CLangMultiDimensionArrayLink2D
implicit none
interface
integer(c_int) function get_table(val) bind(c)
import
type(c_ptr) , value :: val
end function
end interface
Integer , parameter :: M = 6 , N = 4
character(8) ,target :: Results(M,N)
integer :: i
type(CLang2DArrayLink) :: linkResults
i =get_table( linkResults%Set(Results) )
write(*,"(2(a8,1x))") Results(1,1),Results(M,N)
End Program Main
以下模块可以复制粘贴,不做修改。
Module CLangMultiDimensionArrayLink2D
use , Intrinsic :: ISO_C_Binding
Implicit None
private
Type , public :: CLang2DArrayLink
type(c_ptr) , pointer :: p1(:) , p2(:,:)
contains
Procedure :: Set
Final :: Release
End Type CLang2DArrayLink
contains
Type(c_ptr) Function Set( this , datas ) result ( cptr )
class(CLang2DArrayLink) :: this
class(*) , intent(IN) :: datas(1:,1:)
integer :: sizes(2) , i , j
sizes = shape(datas)
if( associated(this%p1) ) deallocate(this%p1,this%p2)
Allocate(this%p1(sizes(2)),this%p2(sizes(1),sizes(2)))
Do i = 1 , sizes(2)
Do j = 1 , sizes(1)
this%p2(j,i) = c_loc(datas(j,i))
End Do
this%p1(i) = c_loc(this%p2(1,i))
End Do
cptr = c_loc(this%p1(1))
End Function Set
Subroutine Release( this )
type(CLang2DArrayLink) :: this
if( associated(this%p1) ) deallocate(this%p1,this%p2)
End Subroutine Release
End Module CLangMultiDimensionArrayLink2D
本帖最后由 andy8496 于 2021-8-16 15:09 编辑
fcode 发表于 2021-7-31 09:05
C函数应该是一个三维数组,你可能需要构建一个指针链给他用。
#include
非常感谢二位的耐心解答。我运行了两个解决方案,都能得到正确的结果。但是,可能是我关于问题的描述/简化不正确,这段时间又在我自己的工程中反复尝试了这两种方法,都没有得到预期的结果。无奈之下,只能在此再次求助!附件是我这个问题的实际工程(VS2012+Intel Parallel Studio XE 2015)。
本帖最后由 布衣龙共 于 2021-8-16 16:17 编辑
原来是 sqlite。sqlite3_get_table 的参数,你给他一个指针,由它内部分配空间,最后把它分配的空间的地址告诉 Fortran。
我们之前的思路,都是由 Fortran 分配空间,构建好,然后交给 C 去填充。
这两者的思路是不同的。
type(c_ptr),target :: val
type(c_ptr),pointer:: p1(:)
istat = sqlite3_get_table(db,"select * from MY_TABLE",val,nRow,nColumn,ErrMsg)
call c_f_pointer(val,p1,)
Do i = 1 , nRow
Do j = 1 , nColumn
call c_f_pointer( p1( (i-1)*nColumn + j ),cRes)
k = index(cRes,c_null_char)
Results(j,i) = cRes(:k-1)
End Do
End Do 布衣龙共 发表于 2021-8-16 16:14
原来是 sqlite。sqlite3_get_table 的参数,你给他一个指针,由它内部分配空间,最后把它分配的空间的地址 ...
cRes我这么声明的
character(kind=c_char,len=256),pointer :: cRes
现在可以了。感激不尽!
另外还有一个问题,这里用到的这些pointer需要释放吗? 可以,没关系的。
Fortran 的指针不需要释放,C那边你找找,可能有别的函数来释放。
页:
[1]
2