通過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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM