使用ctypes可以在python中调用C程序,它提供与C相兼容的数据类型,比如整数类型,浮点数类型,数组等等。这篇文章主要在Linux环境下进行,作为自己日常学习的笔记,如有不对的地方欢迎拍砖。
我们先从最简单的一维数组的例子开始,比如我们有个C函数addOne它的作用是使输入的数组的每个元素+1, 我们想用python程序调用它,从python中向该c函数输入参数,在c程序中将每个值+1之后将数组返还给python程序,该联合的程序主要有两个部分构成:C语言部分和python部分,依次进行说明。
C程序部分的编写和生成链接库
1 //addOne.c2 //这个程序将输入数组的每个元素值+13 //输入参数:数组a,及a的元素个数n
4 void addOne(unsigned char *a,intn)5 {6 for(int i=0;i
我们写好了这个程序之后,我们需要让他成为动态链接库 addOne.so
$ gcc addOne.c -fPIC -shared -o libAddOne.so
其中几个参数的含义是:
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的(参考自http://blog.sina.com.cn/s/blog_54f82cc201011op1.html)
-shared 该选项指定生成动态链接库
-o 指定输出目标名称
在python脚本中调用该库
1 #addOneMain.py2 import ctypes
3 arr=(ctypes.c_uint8*3)(0,1,2)4
5 adder=ctypes.CDLL('./libAddOne.so')6 adder.addOne(arr,3)7
8 for i inrange(0,len(arr)):9 print(arr[i],end=' ')10 print()
该程序首先定义了一个与C相兼容的数组arr,该数组的创建方法是将一个ctypes的基本数据类型乘以一个正整数。之后使用CDLL实例化了一个对象adder,该adder对象中有addOne这个方法。之后将arr这个数组传递给C函数,再将处理结果打印出来。运行上面的python程序可以得到结果为:
1 2 3
由上面的小例子可以引出一下的一些基本知识。
ctypes 常用数据类型
我平时在写程序的时候,经常用到的几个ctypes数据类型分别有:基本数据类型,数组,指针。
基本数据类型
ctypes 中的常用基本数据类型如下表,完整的表格可以参考https://docs.python.org/3.6/library/ctypes.html
ctypes 类型
C 类型
python 类型
c_int8
char
int
c_uint8
unsigned char
int
c_float
float
float
c_bool
_Bool (C99 标准)
bool(1)
数组
ctypes 官方所推荐的构成一维数组的方法是将ctypes中的基本类型乘以一个正整数,比如上述例子中构造一个c_uint8型数组arr,其中包含3个元素,分别是"0,1,2",我们就可以这样构造它
arr=(ctypes.c_uint8*3)(0,1,2)
当然,我们也可以不明确初始化其值,ctypes会默认将所有元素的值设置为0
arr=(ctypes.c_uint8*3)()
这样数组中每个元素的值都是0。在学会使用ctypes声明一维数组之后,我们来讨论一下如何声明二维数组,上个例子中的一维数组,每个元素是一个uint8类型的值,那么二维数组是什么呢,二维数组相当于一个数组的外面又套着一个数组,我们可以通过下面的代码来体会一下。
brr=((ctypes.c_int*2)*3)((ctypes.c_int*2)(1,2),(ctypes.c_int*2)(3,4),(ctypes.c_int*2)(5,6))
通过这样的声明,我们就能得到一个3*2的数组,该数组相当于有一个1*3的数组brr,它的每个元素都是一个1*2的uint8型的数组。当然,我们也可以不先对它赋值,这样ctypes就会自动为每个值赋值为0,即
brr=((ctypes.c_int*2)*3)()
接下来,我们通过一个例子来验证一下,我们在python中声明这个数组brr,然后将它传递给C程序,
1 #initArrayMain.py
2 importctypes3
4 brr=((ctypes.c_int*2)*3)()5 init=ctypes.CDLL('./libInitArray.so')6 init.initArr(brr)7 for i in range(0,6):8 print(brr[int(i/2)][i%2],end=' ')9 if i%2:10 print()
在C程序中将0-5依次赋值给这6个元素
1 //initArray.c
2 void initArr(int arr[][2]){3 for(int i=0;i<6;i++)4 {5 arr[i/2][i%2]=i;6 }7 }
最后运行结果:
0 1
2 3
4 5
指针
将一个ctypes的基本类型的变量通过ctypes.pointer()函数就可以声明一个指向该变量的指针啦
i=ctypes.c_int(8)
pi=ctypes.pointer(i)