python调用c优缺点_应该是史上最全的python调用C接口

本文详细介绍了如何使用Python调用C语言编写的动态链接库(DLL),包括加载DLL、数据类型映射、函数调用、参数传递、结构体交互以及回调函数的实现。内容涵盖各种数据类型、指针、结构体和数组的处理,以及字节流与结构体的相互转换。此外,还特别强调了回调函数中涉及void*和char*类型的处理技巧。
摘要由CSDN通过智能技术生成

这段时间需要用python调用C的接口,网上搜了很多,结合python的官方文档,整理下备用

1、加载dll

from ctypes import *

dll = cdll.LoadLibrary('DLL1.dll')#func1

dll = CDLL('DLL1.dll')#func2

print(dll)

2、数据类型的对应

7b2f2fe3b630?utm_campaign=haruki

3、函数调用

C

DLL1_API int fnDLL1(void)

{

return 42;

}

Python

print(dll.fnDLL1())

4、参数传递

C

DLL1_API int fnDLL2(int a, float b, double c, const char * buffer,int &d)

{

printf("recv : %d,%f,%f,%s,\n", a, b, c, buffer);

d = 10;

return 1;

}

int double float 这些类型可以直接传递

char * 直接传递bytes

指针或者引用类型需要用byref或者pointer,也可以用相应类型的指针类型

例如上个接口中传递 int &d 在传递的过程中可以用 byref(temp)

Python

temp = c_int(0)

print(dll.fnDLL2(1,c_float(2.0),c_double(3.0),'hell0'.encode('gbk'),byref(temp)))

print('byref',temp1.value)

也可以用int的指针类型,这个类型需要自己定义,POINTER一般针对类型

而pointer针对实例化以后的对象,比如上面也可以用pointer(temp)

type_p_int = POINTER(c_int)

temp = type_p_int(c_int(0))

print(dll.fnDLL2(1,c_float(2.0),c_double(3.0),'hell0'.encode('gbk'),temp))

print('int *',temp,temp[0],temp.contents)

返回值

int,float,double 这些类型直接接收就可以

其他类型需要先设置接口的返回类型

C

DLL1_API char * fnDLL3(char *buf)

{

return buf;

}

python

dll.fnDLL3.restype = c_char_p

res = dll.fnDLL3('hello'.encode('gbk'))

print(res,type(res))

如果传递的是char * 需要改变其内容,需要预先定义好存储空间

C

DLL1_API int fnDLL4(char *buf, size_t buffsize)

{

printf("%s\n", buf);

memset(buf, 0, buffsize);

sprintf(buf, "world");

return 1;

}

python

buf = create_string_buffer('hello'.encode('gbk'),10)

dll.fnDLL4(byref(buf),10)

print(buf.value)

unicode类型

C

DLL1_API WCHAR * fnDLL10(WCHAR * buf,size_t bufsize)

{

wprintf(L"wchar:%s\n", buf);

wmemset(buf, 0, bufsize);

wsprintf(buf, L"hello world\n");

return buf;

}

python

wbuf = create_unicode_buffer("hello",32)

dll.fnDLL10.restype = c_wchar_p

res = dll.fnDLL10(byref(wbuf),32)

print("wchar--",res)

5、结构体定义

我们用 fields = [(‘name1’,type1),(‘name2’,type2)]来表示结构体的成员

字节对齐 C结构体中经常会出现按照指定的字节进行对齐结构体,用pack来指定对齐的字节数,数组的定义直接用 *num 表示个数

C

#pragma pack(1)

struct MyStruct

{

int a;

double b;

char c[32];

};

#pragma pack()

python

class MyStruct(Structure):

_fields_ = [

('a',c_int),

('b',c_double),

('c',c_char*32),

]

_pack_ = 1

位域

C

struct MyStruct1

{

int a : 16;

int b : 16;

};

python

class MyStruct1(Structure):

_fields_ = [

('a',c_int,16),

('b', c_int, 16),

]

结构体的嵌套

c

struct MyStruct2

{

int a;

MyStruct S[4];

};

python

class MyStruct2(Structure):

_fields_ = [

('a',c_int),

('struct',MyStruct*4)

]

传递结构体,与之前传递参数一样,指针类型用byref或者pointer

c

DLL1_API int fnDLL5(MyStruct & s)

{

printf("mystruct:\na:%d\nb:%f\nc:%s\n", s.a, s.b, s.c);

return 1;

}

python

mystruct = MyStruct()

mystruct.a = 1

mystruct.b = 1.0

mystruct.c = 'helloworld'.encode('gbk')

dll.fnDLL5(byref(mystruct))

dll.fnDLL5(pointer(mystruct))

返回结构体,与之前相同,需要指定返回的类型

c

DLL1_API MyStruct fnDLL6()

{

MyStruct *tem = new MyStruct;

tem->a = 10;

tem->b = 20;

sprintf(tem->c, "hello");

return *tem;

}

python

dll.fnDLL6.restype = MyStruct

res = dll.fnDLL6()

print(res)

print('mystruct:', res.a, res.b, res.c)

del res

高阶数组的定义

int my_array[10][10];

# 先定义一个数组类型

type_int_array_10 = c_int * 10

# 定义数组的数组(即二维数组)

type_int_array_10_10 = type_int_array_10 * 10

# 创建二维数组对象

my_array = type_int_array_10_10()

# 使用二维数组

my_array[1][2] = 3

字节流与结构体的相互转换

#pack

print(string_at(addressof(mystruct),sizeof(mystruct)))

#unpack

buf = bytes(sizeof(MyStruct))

assert len(buf)

buf = create_string_buffer(sizeof(MyStruct))

res = cast(pointer(buf),POINTER(MyStruct)).contents

print(res,type(res))

print('mystruct:',res.a,res.b,res.c)

def Pack(ctype_instance):

return string_at(addressof(ctype_instance),sizeof(ctype_instance))

def UnPack(ctype,buf):

assert sizeof(ctype) == len(buf)

cstring = create_string_buffer(buf)

return cast(pointer(cstring),POINTER(ctype)).contents

回调函数

先用CFUNCTYPE 定义回调函数类型,参数的第一个参数为返回值类型

后面的参数为回调函数传递的参数类型,然后定义python中的函数,

C

typedef int (*callbakc) (int a, int b);

DLL1_API void fnDLL7(int a, int b, callbakc func)

{

int n = func(a, b);

printf("c++ callback %d\n", n);

}

python

CMPFUNC = CFUNCTYPE(c_int,c_int,c_int)

cmp_func = CMPFUNC(callFunc)

dll.fnDLL7(1,2,cmp_func)

这里有个地方特别注意,如果回调函数中有void* ,char等类型,在python中定义回调函数的时候如果定义为 c_void_p ,c_char_p,实际返回的数据为int,bytes

这时候其实python内部已经把参数的值拿出来了,而我们需要的是char地址的内容,常用的比如传递某一串字节流,我们需要传递出字节流的长度和首地址的指针,如果直接使用参数,c_void_p拿到的是一个int类型,而c_char_p拿到的是截止到最后一个'\0'的字节,最终我们在python中用string_at 来拿到实际的字节流

c回调

typedef void (*callbakc) (void * buf, int &buf_size);

python中的定义

def callback(buf,size):

string = string_at(buf,size.value)

CALLBACKFUNC = CFUNCTYPE(None,c_void_p,c_int)

call = CALLBACKFUNC(callback)

OK,应该基本都讲到了,后续有遇到的坑再继续填

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值