跳转至

python调用c踩坑记录

python调用c踩坑记录

最近项目需求,使用python调用c语言写的so库

采用了python的ctypes模块

总结遇到的一些坑

要点

类型关系

  • 要注意python和c语言的变量类型的对应关系,这在高级语言和调用c语言上是特别重要的,在java调用c语言使用JNA时也尤其重要。
  • 使用最多且尤其注意的是指针类型,如ctype类库中有c_void _p()、c_char_p()两种指针类型。除了自带的以上两种,还可以通过在python中创建一个类继承ctypes.Structure类,然后使用ctype.POINTER获取其指针。

python的对象生命周期

  • python在使用return返回值时创建一个对象,那么在返回以后即函数结束后就会释放对象。
  • python调用c语言的对象时,c语言(通extern “C”将c++按照c语言编译就可以使用new创建堆上的对象了)通过new创建一个堆上的对象,然后将将指针以void *类型返回给python。如果以后都不使用对象,必须在c类库这边封装dispose函数(用于调用delete释放new创建的资源),然后通过python调用c封装的dispose函数释放资源。一开始,准备将调用dispose的代码写在python的一个封装的指针对象的__delete__()函数中,但是由于第一条,则选择了写在一个封装的指针对象的dispose()函数中,手动调用dispose()函数进行资源释放。

回调函数

  • python将函数通过指针的形式传给c类库中函数指针,然后在c类库中进行调用。
  • 观察到python回调函数中打印的c类库中指针的地址比普通函数中长,是因为python回调函数是在c类库中调用的,所以是在栈空间。普通函数在python这边执行,是c类库new的对象指针传递过来的,所以在堆上。所以在python的回调函数中,要指定调用c类库函数时传入的参数指针的长度和返回值的指针的长度,可以通过函数名.retypes = ctypes.c_u_int_64,函数名.argstypes = [c_void_p,c_void_p]指定,不然python会将地址长度切断,地址变短,指针值错误,找不到指针。
  • 在回调函数传入时,python的回调函数要加上self. 或者 设置为全局变量,延长回调函数的生命周期,不然注册函数结束后,回调函数就被回收了,c类库那边将找不到,发生段错误。

未完待续,后面将补充例子进行解释…