通过ctypes向C程序传递一维和二维数组(Linux环境下)

使用ctypes可以在python中调用C程序,它提供与C相兼容的数据类型,比如整数类型,浮点数类型,数组等等。这篇文章主要在Linux环境下进行,作为自己日常学习的笔记,如有不对的地方欢迎拍砖。

我们先从最简单的一维数组的例子开始,比如我们有个C函数addOne它的作用是使输入的数组的每个元素+1, 我们想用python程序调用它,从python中向该c函数输入参数,在c程序中将每个值+1之后将数组返还给python程序,该联合的程序主要有两个部分构成:C语言部分和python部分,依次进行说明。

C程序部分的编写和生成链接库

 1 //addOne.c
 2 //这个程序将输入数组的每个元素值+1
 3 //输入参数:数组a,及a的元素个数n
 4 void addOne(unsigned char *a,int n)
 5 {
 6     for(int i=0;i<n;i++)
 7     {
 8         a[i]++;
 9     }
10 }

我们写好了这个程序之后,我们需要让他成为动态链接库 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.py 
2
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 in range(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 import ctypes
 3 
 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)

 

posted @ 2018-11-21 19:13  食人魔法师  阅读(4349)  评论(0编辑  收藏  举报