Python 之 ctypes模块 ——C语言的数据类型

本文介绍了Python中如何通过ctypes模块使用C类型的数组,包括c_int、c_char等,并展示了如何创建和操作这些数组,以及如何进行数组的倒序操作。此外,还探讨了C类型数组与Python list的区别,强调了C类型数组长度不可变但元素可变的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python中使用c类型的数组

Python 在 ctypes 中为我们提供了类似C语言的数据类型,它的用途(作者的理解)可能是:

  1. 其他语言(如 C、Delphi 等)写的动态连接库DLL 进行交换数据,因为 pythonDLL的数据难以进行数据交换(直接交换)。

  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 与之相应

(3) c_double 、c_float

  C 类型的 double , 这两个名字( c_double 、c_float 完全相同 )

  占用 8 字节内存   

  python 用 float 与之相应

(4) c_byte

  C 类型 的 byte ,   占用1字节内存
  
  python 用 int 与之相应

(5) c_char

   C 的 8 bit 字符型   

() 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) ).   // byref(parameter)  一个指针类型的 形参

struct例子
下面的例子是定义一个结构
C语言例子

	// C语言 创建结构的方式
    struct beer_recipe
    {
    int amt_barley;
    int amt_water;
    };

Python例子

# 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							#Python 初始化一个10个元素的数组,初始值=0
>>> for i in range(0, len(a)):
          print( a[i], end=" ")
  
0 0 0 0 0 0 0 0 0 0 
>>>

(二) 生成 10 元素的 c_int 类型的数组:

格式一:

 #  ctypes.c_int类型 的数组
 
>>> from ctypes import *
>>> a = ( c_int * 10) ()					
# 初始化 c_int 类型的10个数组元素(初始值=0),并成为引用给a变量

>>> 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 ) ()
# 格式一的变种,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			# 表示 c_int类型且长度为10的数组类型,注意这里并没有实例化
>>> a = myArr10( )						# myArr10( ) 实例化该数组类型,并将其引用赋值给a
# 在格式二的基础上更近一步,先创建了一个固定的数组类型

>>> 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
        # Python这个写法很有诡异,语法上缺少了一个变量
        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 是一个真正的数组(区别与Python的list类型,本质区别:非引用),我们可以对它的每个元素(字符)进行修改。

从 ar 中取出 python 的字符串:

  s = ar.value
  # ar.varue属性
  # 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 ] = [] 格式的语句来删除某个子数组,Python list的其他方法都可以使用(有待确认??);Python可以使用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] 的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值