1. 程式人生 > >python 中如何使用 C 型別的陣列? ctypes 的用法

python 中如何使用 C 型別的陣列? ctypes 的用法

Python 在 ctypes 中為我們提供了類似C語言的資料型別,

它的用途(我理解的)可能是:

(1) 與 其他語言(如 C、Delphi 等)寫的動態連線庫DLL 進行交換資料,因為 python 的 資料與 DLL難以進行資料交換。

(2) python 的字串一旦形成,是不可變的,為了演算法的需要,我們有時需要對字串進行原位操作 ( in place ),而不想浪費另外的記憶體空間。

(3) python 具有很簡明的語法,人們樂於使用。在解決實際問題時,字串的處理佔據了很大的開發量。

     網際網路上有很多有用的演算法可以幫助我們解決問題,如果我們能用python 寫類似於 C 語言的程式,就不需要用其他語去寫擴充套件

了。

     有人會問,既然如此,用C語言,不就好了嗎?

      當然可作這種選擇,在用 python 的優勢在於:既用使用了C語言的優點,也使用了Python的最大優點: 垃圾自動回收,程式碼簡潔等。

一、 匯入 C 型別 庫

from ctypes import *

二、 常用的C 型別

(1) c_int   、 c_long 、c_int32

      C 型別的long int ,這兩個型別完全相同。

       python 用 int 與之相應 , 但c_int的取值範圍是 32 bit 的整數 。

      佔用 4 位元組記憶體

   (2) c_int64

      64 bit 整數,佔用 8 位元組記憶體    , python 用 int 與之相應

   (2) c_double 、c_float

      C 型別的 double , 這兩個名字( c_double 、c_float 完全相同 )

      佔用 8 位元組記憶體   

       python 用 float 與之相應

(3) c_byte

      C 型別 的 byte ,   python 用 int 與之相應

      佔用1位元組記憶體

   (4) c_char

       C 的 8 bit 字元型   

(5) c_wchar

      C 的 unicode 字元

【注】

ctypes模組

C型別                       Python型別                        ctypes 型別
char                        1-character/string                c_char
wchar_t                     1-character/Unicode、string       c_wchar
char                        int/long                          c_byte
char                        int/long                          c_ubyte
short                       int/long                          c_short
unsigned short              int/long                          c_ushort
int                         int/long                          C_int
unsigned int                int/long                          c_uint
long                        int/long                          c_long
unsigned long               int/long                          c_ulong
long long                   int/long                          c_longlong
unsigned long long          int/long                          c_ulonglong
float                       float                             c_float
double                      float                             c_double
char *(NULL terminated)     string or none                    c_char_p
wchar_t *(NULL terminated) unicode or none                   c_wchar_p
void *                      int/long or none                  c_void_p

當一個函式期望一個指標作為引數時,可以像這樣呼叫
function_main( byref(parameter) ). //

struct例子
下面的例子是定義一個結構
C語言例子
    struct beer_recipe
    {
    int amt_barley;
    int amt_water;
    };
    
Python例子
class beer_recipe(Structure):
    _fields_ = [
    ("amt_barley", c_int),
    ("amt_water", c_int),
    ]
    
    
Union結構例子
C語言例子
    union {
    long barley_long;
    int barley_int;
    char barley_char[8];
    }barley_amount;

Python例子
class barley_amount(Union):
    _fields_ = [
    ("barley_long", c_long),
    ("barley_int", c_int),
    ("barley_char", c_char * 8),
    ]

三、    生成類似C的陣列

目的:初值化一個具有 10 個元素 的陣列,每個元素初值為0的

(一) python 原生陣列 list

>>> a = [ 0 ] * 10
>>> for i in range(0, len(a)):
          print( a[i], end=" ")


0 0 0 0 0 0 0 0 0 0 
>>>

(二) 生成 10 元素的 c_int 型別的陣列:

格式一:

>>> from ctypes import *
>>> a = ( c_int * 10) ()
>>> for i in range(0, len(a)):
        print( a[i], end=" ")


0 0 0 0 0 0 0 0 0 0 
>>>

格式二:

>>> from ctypes import *
>>> M = 10
>>> a = ( c_int * M ) ()
>>> for i in range(0, len(a)):
    print( a[i], end=" ")


0 0 0 0 0 0 0 0 0 0

格式三:

>>> from ctypes import *
>>> myArr10 = c_int * 10
>>> a = myArr10( )
>>> for i in range(0, len(a)):
     print( a[i], end=" ")

     
0 0 0 0 0 0 0 0 0 0

c_double 的陣列定義與上面相似。

四、如何使用 C 型別的陣列 ?

例 1 , 對整數陣列倒序的程式

#coding=gbk
from ctypes import *
# 定義 具有10個 c_int 元素的陣列
# 編寫一維陣列的 倒序的程式
# 說明 : 本演算法參照 網上基於指標的演算法改寫而成。
def outPut( A ):
    for i in range(0,N):
          print( A[i], end=" ")
    print ( "\n")
    
def arrReverse( A , N):
    i = 0 ; j = N-1
    while i<j:
        A[i], A[j] = A[j], A[i]
        # 相當於 T =A[i]; A[i]=A[j]; A[j]=T
        i = i+1; j=j-1
    
#測試程式
N = 10
a = (c_int * N )()
for i in range(0,N):
    a[i] = i;
print ( "原陣列:")    
outPut( a )
arrReverse( a ,len(a) )
print ("倒序陣列:")
outPut( a )

--- 結果 ---

原陣列:
0 1 2 3 4 5 6 7 8 9

倒序陣列:
9 8 7 6 5 4 3 2 1 0

例2 求倒序字串

#coding=gbk
from ctypes import *
# 編寫求字串的倒序字串 
def arrReverse( A , N ):
    i = 0 ; j = N-1
    while i<j:
        A[i], A[j] = A[j], A[i]
        # 相當於 T =A[i]; A[i]=A[j]; A[j]=T
        i = i+1; j=j-1
    
#測試程式
        
a = create_unicode_buffer( "張三買了一頭小毛驢,花了1024.05元錢。")

print ( "原字元:")    
print ( a.value )
arrReverse( a , len( a ) -1 )
print ("倒序字串:")
print ( a.value )

-- 結果 --

原字元:
張三買了一頭小毛驢,花了1024.05元錢。
倒序字串:
。錢元50.4201了花,驢毛小頭一了買三張

解說

(1) create_unicode_buffer( python的字串 )

    是建立一個 c_wchar 的陣列,其長度是 字串的長度 +1 , 因為 C 的字串是以 NULL 結尾的所以要多出一個元素才行。

   c_wchar 是 unicode 字元。

(2)   如果您想建立一個可以裝行下 100 個 unicode 字元 的空的C_wchar 陣列:

   ar = create_unicode_buffer( 100+1 )

此時, ar 具有 101 個元素,但只能裝 100個字元。

由於 ar 是一個真正的陣列,我們可以對它的每個元素(字元)進行修改。

從 ar 中取出 python 的字串:

      s = ar.value

      s 中存放的是 ar 中儲存的 unicode 字元相應的字串

向 ar 中存於字串

     ar.value = "要存入的字串"

但要注意:

    您向 ar 中存入的字串的字元個數必須小於等於 len(ar) -1

(3) 如果您知道 ar 中第 i 個元素的 字元編碼,請使用 ord( ar[i] ) .

(4) 如果您想使 ar 的第 i 個元素 變為 "好" 這個字元,有兩種方法:

    ar[i] = "好"

    或者

    ar[i] = chr(22909)

    因為 , "好" 的 uncode 編碼是 22909。

小結:

(1) 從這個程式我們看到 對整數陣列及 unicode 字元陣列 的倒序,我們用的是相同的arrReverse 函式。

(2) 呼叫 arrReverse

函式時,傳遞元素個數 N 時,如果是 c_wchar ( unicode ) 字元陣列時, 如記住最後一個元素是 NULL 這個問題。

( 3 ) 您會發現,我們這裡編的程式很像 C 語言的指標,只不過,我們不需要手工釋放動態申請的陣列。

( 4 ) 但是您的演算法中若用了大量的動態陣列,等不及垃圾自動回收,而急於想釋放陣列佔用的空間時,請使用 del ( ar )即可。

( 5) 最後一點:

     C 語言關於字串的操作,常 使用指標的移動, 我們在 python 中移動的是陣列的下標,這是作程式移植時常用的方法。

五、 C型別的陣列 與 python 的 list 用法上有什麼區別和聯絡呢?

1 C 型別的陣列的長度是不可變 的。

2 C 型別陣列的元素是可變的,即可以讀寫的

3 C 型別陣列的元素是有型別的,即: 它的每個元素的型別是相同的, 而 python 的 list 的元素可以是相同型別,也可以是不同型別 。

4 C 型別陣列除了不能用 形如 ar[ 3:5 ] = [] 格式的語句來刪除某個子陣列

5 C 型別陣列的切片:

   如 x = ar[3:5]

   此時 x 是一個全新的 陣列,它是原陣列的完全拷貝, 此時 x 有兩個元素:

   x[0] 中存的是 ar[3]的值

   x[1] 中存的是 ar[4]的值   

    此後,改變 x[0] 時, ar[3] 不會改變; 改變 ar[3] 的值,也不會改變 x[0] 的值。