通過ctypes向C程序傳遞一維和二維數組(Linux環境下)

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章