【高级教程】ctypes:从python菜鸟到c大神2

上一篇主要从理论指导上介绍ctypes,这一篇将从操作指南方面介绍ctypes。相当多的语句特性、细节都体现在例子中,请仔细分析。诸位的批评教育就是我进步上升空间,欢迎指教。

目录

1、ctypes实用函数

1.1 addressof、alignment、byref、cast

1.2 create_string_buffer、create_unicode_buffer

1.3 DllCanUnloadNow、DllGetClassObject

1.4 find_library、find_msvcrt

1.5 FormatError、GetLastError、get_errno、get_last_error

1.6 set_errno、set_last_error

1.7 memmove、memset、POINTER、pointer、resize

1.8 sizeof、string_at、wstring_at、WinError

2、数据类型

2.1 _CData

2.1.1 公共方法

2.1.2 公共属性

2.2 基础数据类型

2.2.1 _SimpleCData

2.2.2 基础数据类型

2.3 结构化数据类型

2.3.1 _fields_

2.3.2 _pack_

2.3.4 _anonymous_

2.4 array和pointer


1、ctypes实用函数

>>> from ctypes import *
>>>

1.1 addressof、alignment、byref、cast

addressof(obj):返回ctypes类型实例obj的内存块地址(整数)。

alignment(obj_or_type):返回ctypes类型或实例obj_or_type所要求的对齐。

byref(obj[,offset]):返回一个指向ctypes类型实例obj的轻量级指针。offset是一个整数,默认为0,表示相对于内部指针(首地址)的偏移。

byref(obj,offset)对应c代码如下:

(((char*)&obj)+offset)

byref()函数返回值只能用于外部函数参数。其行为类似于pointer(obj),但要快得多。

cast(obj,type):函数作用类似于c类型转换运算,返回一个指向obj内存块的type类型pointer实例。type必须是一个pointer类型,并且obj必须能解析为pointer对象。

>>> a=c_int(90)
>>> addr=addressof(a)
>>> s=c_char_p(addr)
>>> s.value
b'Z'
>>> a.value=65
>>> s.value
b'A'
>>> alignment(a)
4
>>> alignment(c_double)
8
>>> class POINT(Structure):
      _fields_=[('x',c_int),('y',c_int)]
>>> p1=POINT(540,960)        
>>> print(POINT.y)     

>>> def sayHi(Cint):
           print('Hi',cast(Cint,POINTER(c_int)).contents.value)
>>> sayHi(byref(p1,4))
Hi 960
>>>

1.2 create_string_buffer、create_unicode_buffer

create_string_buffer(init_or_size,size=None)

创建一个可写内存块,返回c_char数组。init_or_size必须是一个整数以指明数组大小,或者是一个初始化数组各项的字节串对象

如果init_or_size是字节串对象,数组将在长度上大于字节串对象,以使最后一个元素为NUL终止符。如果字节串对象仅用于初始化数组元素,则可用第二个参数size指定数组长度。源码:

def create_string_buffer(init, size=None):

    if isinstance(init, bytes):

        if size is None:

            size = len(init)+1

        buftype = c_char * size

        buf = buftype()

        buf.value = init

        return buf

    elif isinstance(init, int):

        buftype = c_char * init

        buf = buftype()

        return buf

    raise TypeError(init)

create_unicode_buffer(init_or_size,size=None)

创建一个可写内存块,返回c_wchar数组。init_or_size必须是一个整数以指明数组大小,或者是一个初始化数组各项的字符串(str)对象

如果init_or_size是字符串对象,数组将在长度上大于字符串对象,以使最后一个元素为NUL终止符。如果字符串对象仅用于初始化数组元素,则可用第二个参数size指定数组长度。源码:

def create_unicode_buffer(init, size=None):

    if isinstance(init, str):

        if size is None:

            size = len(init)+1

        buftype = c_wchar * size

        buf = buftype()

        buf.value = init

        return buf

    elif isinstance(init, int):

        buftype = c_wchar * init

        buf = buftype()

        return buf

    raise TypeError(init)

create_string_buffercreate_unicode_buffer最大不同之处在于返回的数组类型(一个字符元素几个字节)。当一个函数希望能够修改某个参数所指内存块内容时,不能传递c_char_p、c_wchar_p、c_void_p实例,而应传递create_string_buffer/create_unicode_buffer创建的实例或其他POINTER/pointer/array实例。

>>> a=b'abcde'      
>>> pi=c_char_p(a)     
>>> pi.value
b'abcde'
>>> pi.value=b'cdef'
>>> a
b'abcde'
>>> pi=create_string_buffer(a)
>>> pi.value
b'abcde'
>>> pi.raw
b'abcde\x00'
>>> pi.value=b'123'
>>> pi.value
b'123'
>>> pi.raw     
b'123\x00e\x00'
>>> a       
b'abcde'
>>> a=create_unicode_buffer('abcde')            
>>> a.value           
'abcde'
>>> a.raw
AttributeError: 'c_wchar_Array_6' object has no attribute 'raw'
>>> sizeof(a)         
12
>>>

valueraw的区别:前者所见即NUL串,后者所见即所得(数组内容)。

1.3 DllCanUnloadNow、DllGetClassObject

这两个函数是仅限windows的钩子函数,支持通过ctypes实现in-process COM服务器(DLL运行在包含自身的应用程序进程空间)。内部将调用comtypes.server.inprocserver导出的同名函数。

1.4 find_library、find_msvcrt

ctypes.util.find_library(name):查找函数库并返回其路径名。name是不带任后前缀(如lib)、后缀(如.so,.dylib)或版本号(由posix链接选项-l生成)的库名,查找失败时返回None。本函数实际功能依赖于系统。

ctypes.util.find_msvcrt():仅限windows,返回python及扩展模块使用的VC运行时库文件名。如果函数库名未定义,则返回None。

如果要释放某个扩展模块分配的内存,则使用与内存分配函数同库的内存释放函数(例如free(void*))相当重要。

1.5 FormatError、GetLastError、get_errno、get_last_error

ctypes.FormatError([code]):仅限windows,返回错误代码code的文本描述。如果未指定code,则使用windows API函数GetLastError()获取的错误代码。

ctypes.GetLastError():仅限windows,返回所在线程windows设置的最近错误代码。此函数直接调用windows API GetLastError(),而不会返回错误代码的ctypes私有副本。

ctypes.get_errno():返回所在线程系统变量errno的ctypes私有副本当前值。

ctypes.get_last_error():仅限windows,返回所在线程中系统变量LastError的ctypes私有副本当前值。

1.6 set_errno、set_last_error

ctypes.set_errno(value):将所在线程中系统变量errno的ctypes私有副本当前值设置为value,并返回设置前的值。

ctypes.set_last_error(value):仅限windows,将所在线程中系统变量LastError的ctypes私有副本当前值设置为value,并返回设置前的值。

1.7 memmove、memset、POINTER、pointer、resize

ctypes.memmove(dst, src, count):同标准c函数库memmove函数,从src复制count字节到dst。src和dst必须是整数或可转换成pointer的ctypes实例。

>>> src=(c_ubyte*5)(0xee,0xdd,0xcc,0xbb,0xaa)
>>> dst=c_ulonglong()
>>> memmove(addressof(dst), src,5)
2752999090960
>>> hex(dst.value)
'0xaabbccddee'
>>>

ctypes.memset(dst, c, count):同标准c函数库memset函数:从dst开始的内存块开始填充count字节c。dst必须是一个指定地址的整数或ctypes实例。

>>> memset(byref(dst,5),0xff,3)
2213290994581
>>> hex(dst.value)
'0xffffffaabbccddee'
>>>

ctypes.POINTER(type):创建并返回一个新ctypes指针类型。该指针类型将被缓存以在内部重复使用,所以再次使用时效率更高。type必须是ctypes类型。

>>> lpint=POINTER(c_int)
>>> a=c_int(9)
>>> la=lpint(a)
>>> la.contents.value=900
>>> a
c_long(900)
>>> a.value=90
>>> la.contents.value
90
>>> clpint=POINTER(c_int)
>>> lpint is clpint
True
>>>

ctypes.pointer(obj):创建一个指向obj的pointer实例,其类型为POINTER(type(obj))。

>>> lla=pointer(la)
>>> lla.contents.contents.value
90
>>> type(lla)==POINTER(POINTER(c_int))
True
>>>

ctypes.resize(obj, size):重新分配obj(必须是ctypes类型实例)内部内存块大小。size(字节)不得小于obj类型原始大小sizeof(type(obj))。

>>> a=c_int(256)
>>> resize(a,8)
>>> bts=cast(byref(a),POINTER(c_ubyte))
>>> bts[1]
1
>>> bts[1]=0
>>> bts[7]=0x80
>>> 2**63
9223372036854775808
>>> a
c_long(0)
>>> ul=cast(byref(a),POINTER(c_ulonglong))
>>> ul.contents.value
9223372036854775808
>>>

上例中cast()有很多种等价用法,如cast(byref(a),POINTER(c_ubyte))、cast(addressof(a),POINTER(c_ubyte))、cast(pointer(a),POINTER(c_ubyte)),还有一种特殊用法:

>>> a=c_int(256)
>>> resize(a,8)
>>> bts=cast(byref(a),POINTER(c_ubyte*8))
>>> bts[0][1]=0
>>> bts[0][7]=0x80
>>> ul=cast(byref(a),POINTER(c_ulonglong))
>>> ul.contents.value
9223372036854775808
>>>

1.8 sizeof、string_at、wstring_at、WinError

ctypes.sizeof(obj_or_type):同c操作符sizeof,返回obj_or_type(ctypes类型或内存块实例)大小(字节)。

>>> s=create_string_buffer(b'abcde')
>>> sizeof(s)
6
>>> len(s)
6
>>> ws=create_unicode_buffer('abcde')
>>> sizeof(ws)
12
>>> len(ws)
6
>>> sizeof(c_int)
4
>>>

ctypes.string_at(address, size=-1):返回从内存地址address开始的c字符串(bytes对象)。如果指定size,则c字符串大小为size(字节),否则假定字符串以单NUL字符终止。

>>> bc=string_at(s)
>>> bc
b'abcde'
>>> bc=string_at(s,7)
>>> bc
b'abcde\x00\x00'
>>>

ctypes.wstring_at(address, size=-1):返回从内存地址address开始的宽字符串(str对象)。如果指定size,则宽字符串长度为size(个),否则假定宽字符串以宽NUL字符终止。

>>> wc=wstring_at(ws)
>>> wc
'abcde'
>>> ws[5]
'\x00'
>>> ws[6]
IndexError: invalid index
>>> wc=wstring_at(ws,7)
>>> wc
'abcde\x00\x00'
>>> len(wc)
7
>>>

ctypes.WinError(code=None, descr=None):仅限windows,创建一个OSError实例。如果未指定code,则调用GetLastError()确定错误代码。如果未指定descr,则调用FormatError()获取错误代码的文件描述。

>>> def error():
      return WinError(1)
>>> a=error()
>>> a
OSError(22, '函数不正确。', None, 1)
>>>

2、数据类型

2.1 _CData

class ctypes._CData:非公开类_CData是所有ctypes数据类型的共同基类。此外,所有ctypes类型实例都包含一个存放c兼容数据的内存块,其地址由addressof()函数返回。另一个开放的实例变量是_objects,该变量包含需存活的其他python对象(以防内存块含有pointer)。

>>> a=c_int(90)
>>> a._objects
>>> p=pointer(a)
>>> p._objects
{'1': c_long(90)}
>>> b=cast(p,POINTER(c_int))
>>> p._objects
{'1': c_long(90), 2796355363656: <__main__.LP_c_long object at 0x0000028B13C4A348>}
>>> b._objects
{'1': c_long(90), 2796355363656: <__main__.LP_c_long object at 0x0000028B13C4A348>}
>>> b._objects[2796355363656].contents.value
90
>>>

2.1.1 公共方法

下面是ctypes数据类型公用方法(都是类方法,准确点讲,都是元类方法):

from_buffer(source[,offset]):返回共享source对象内存块的ctypes实例。source必须提供内存块写接口。可选参数offset指定source内存块偏移(字节),默认为0。如果source内存块不够大将抛出ValueError异常。

>>> a=memoryview(bytearray(b'a'))
>>> a[0]
97
>>> b=c_char.from_buffer(a)
>>> b
c_char(b'a')
>>> b.value=b'b'
>>> a[0]
98
>>> bts=bytearray(10)
>>> c=(c_char*10).from_buffer(bts)
>>> c.value
b''
>>> c.value=b'abcdef'
>>> bts
bytearray(b'abcdef\x00\x00\x00\x00')
>>> bts=bytearray(b'0123456789')
>>> arr_size=6
>>> for offset in range(1,7):
      try:
             cas=(c_char*arr_size).from_buffer(bts,offset)
      except ValueError as e:
             v=str(e).split()
             v[-2]=str(v[-2])+"*offset+arr_size*"
             ve=" ".join(v)
             print("offset={} ".format(offset),ve)
             continue
      print(cas.value)   
b'123456'
b'234567'
b'345678'
b'456789'
offset=5  Buffer size too small (10 instead of at least 11*offset+arr_size* bytes)
offset=6  Buffer size too small (10 instead of at least 12*offset+arr_size* bytes)
>>>

from_buffer_copy(source[, offset]):基本同from_buffer,主要区别为所创新ctypes实例内存块为source对象内存块(必须可读)副本。

>>> a=memoryview(bytearray(b'a'))
>>> a[0]
97
>>> b=c_char.from_buffer_copy(a)
>>> b
c_char(b'a')
>>> b.value=b'b'
>>> a[0]
97
>>>

from_address(address):返回使用指定内存块地址address(整数)创建ctypes实例。

>>> a=c_int(90)
>>> b=c_int.from_address(addressof(a))
>>> b.value
90
>>> b.value=900
>>> a.value
900
>>> a is b
False
>>>

from_param(obj):类方法,将obj调配成一个ctypes类型实例。当obj的类型出现在外部函数的argtypes元组,from_param(obj)将在调用外部函数时以实际对象为参数被调用,并返回一个可作为外部函数参数的对象。

from ctypes import *
libc=cdll.msvcrt
printf=libc.printf
class Bottles:
    def __init__(self,number):
        self.number=number
    @property
    def _as_parameter_(self):
        return self.number
    @classmethod
    def from_param(cls,obj):
        if obj.__class__!=cls:
            raise AttributeError('Not a Bottles.')
        return obj
bottles=Bottles(42)
printf.argtypes=[c_char_p,c_char_p,Bottles,c_int]
printf(b'Hi,%s,I have %d bottles.%d ?',b'James',bottles,100)

所有ctypes数据类型都有默认定义的from_param(obj),一般情况下返回obj(如果obj是该数据类型实例)。一些类型也能接受其他对象。

>>> a=c_int(90)
>>> c_int.from_param(a)
c_long(90)
>>> c_int.from_param(a) is a
True
>>> c_float.from_param(0.9)

>>> c_float.from_param(9)

>>>

in_dll(library, name):返回共享库library导出的ctypes实例name。

>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> opt_flag
c_long(0)
>>>

2.1.2 公共属性

_b_base_:有些ctypes数据实例本身并不拥有内存块,而是共享基类部分内存块。只读成员_b_base_代表包含内存块的根ctypes对象。

_b_needsfree_:只读成员,当ctypes数据实例有自身内存块时为True,否则为False。

_objects:一个包含python对象的字典,或者为None,或者包含需要留存的python对象,以让内存块内容有效。该字典仅用于调试,绝对不要修改其内容。

2.2 基础数据类型

2.2.1 _SimpleCData

class ctypes._SimpleCData:非公开类,所有ctypes基础数据类型基类,包含所有ctypes基础数据类型共同属性。_SimpleCData是_CData子类,继承其属性与方法。不是也不含pointer的ctypes数据类型可被序列化。

value:_SimpleCData唯一属性,包含实例实际值。对于整数和指针(pointer)类型,其值为整数;对于字符类型,其值为单字符bytes对象或单字符str对象;对于字符pointer类型,其值为python bytes对象或str对象。

当从一个ctypes实例获取(访问)value的时候,通常每次返回一个新对象。ctypes并未实施OOR(原始对象返回),总是会构造一个新对象。这对所有其他ctypes实例对象都一样。

2.2.2 基础数据类型

当作为一个外部函数返回结果返回的时候,或者,比如说,在获取structure字段成员或array元素的时候,基础数据类型将被转换成原生python类型。换句话说,如果一个外部函数restype属性为c_char_p,其返回结果将总是python bytes对象,而不是c_char_p实例。

基础数据类型子类不会继承这一行为。所以,如果一个外部函数restype属性为c_void_p子类,其返回结果将是c_void_p子类实例。此时,可以通过访问其value属性来获取该指针对象的值。

基础数据类型

c_byte

 signed char,其值解析为小整数,实例化参数可以是整数,不进行溢出检查。

c_char

char,其值解析为单bytes字符,实例化参数可以是bytes字符串,参数长度只能是1。

c_char_p

char*,None或NUL字符串,为让普通字符pointer也能指向二进制数据,必须使用POINTER(c_char)转换。实例化参数可以是地址(整数),或bytes对象。

c_double

double,实例化参数可以是float对象。

c_longdouble

long double,实例化参数可以是float对象,在sizeof(long double)==sizeof(double)的平台上为c_double别名。

c_float

float,实例化参数可以是float对象。

c_int

int,实例化参数可以是整数,不进行溢出检查,在sizeof(int)==sizeof(long)平台上为c_long别名。

c_int8

8-bit signed int,通常作为c_byte别名。

c_int16

16-bit signed int,通常作为c_short别名。

c_int32

32-bit signed int,通常作为c_int别名。

c_int64

64-bit signed int,通常作为c_longlong别名。

c_long

signed long,实例化参数可以是整数,不进行溢出检查。

c_longlong

signed long long,实例化参数可以是整数,不进行溢出检查。

c_short

signed short,实例化参数可以是整数,不进行溢出检查。

c_size_t

size_t。

c_ssize_t

ssize_t。

c_ubyte

unsigned char,其值解析为小整数。实例化参数可以是整数,不进行溢出检查。

c_uint

unsigned int,实例化参数可以是整数,不进行溢出检查,在sizeof(int)==sizeof(long)平台上为c_ulong别名。

c_uint8

8-bit unsigned int,通常作为c_ubyte别名。

c_uint16

16-bit unsigned int,通常作为c_ushort别名。

c_uint32

32-bit unsigned int,通常作为c_uint别名。

c_uint64

64-bit unsigned int,通常作为c_ulonglong别名。

c_ulong

unsigned long,实例化参数可以是整数,不进行溢出检查。

c_ulonglong

unsigned long long,实例化参数可以是整数,不进行溢出检查。

c_ushort

unsigned short,实例化参数可以是整数,不进行溢出检查。

c_void_p

void*,其值解析为整数,实例化参数可以是整数(地址)。

c_wchar

wchar_t,其值解析为单字符unicode串,实例化参数可以是字符串,长度必须为1。

c_wchar_p

wchar_t*,指向NUL宽字符串的pointer,实例化参数可以是整数(地址),或字符串。

c_bool

bool(更准确些,从C99开始为_Bool),其值可以是True或False,实例化参数可以是有实值的任意对象。

HRESULT

HRESULT(windows-only),包含函数、方法调用成功或出错信息。

py_object

PyObject*,实例化时不带参数将创建值为NULL的 PyObject* pointer。

ctypes.wintypes模块提供相当多的windows特殊数据类型,比如HWND,WPARAM,或DWORD,此外还定义了一些非常有用的结构,如MSG或RECT。

2.3 结构化数据类型

class ctypes.Union(*args, **kw):抽象基类,本地字节序union。

class ctypes.BigEndianStructure(*args, **kw):抽象基类,大端字节序structure。

class ctypes.LittleEndianStructure(*args, **kw):抽象基类,小端字节序structure。

非本地字节序structure不能包含pointer字段,或任何含有pointer字段的数据类型。

class ctypes.Structure(*args, **kw):抽象基类,本地字节序structure。

具体的structure/union实例必须由上述类型的子类创建,并至少定义一个类属性(即_fields_)。ctypes将创建直接通过属性访问形式以支持字段读写的描述符。

2.3.1 _fields_

_fields_:定义结构字段的序列。序列各元素必须是二元组或三元组。元素第一项是字段名,第二项是字段类型(可以是任意ctypes数据类型)。如果字段类型是c_int这样的整数,则可以指定第三项(可选,必须是一个整数,定义字段位宽)。

structure或union中的字段名必须唯一。ctypes不会检查这种唯一性,当有字段重名时将只有一个字段可被访问。

可以在structure子类定义语句之后赋值类属性_fields_,这将允许创建直接或间接引用自身的数据类型。

>>> class List(Structure):pass
>>> List._fields_ = [("pnext", POINTER(List))]
>>>

无论如何,_fields_类属性必须赋值于所在类型被使用(创建一个实例,调用sizeof()求其大小等)之前。如果是在使用之后再赋值_fields_将抛出AttributeError。

可以创建structure子类的子类,子类的字段将包括继承自所有基类的字段以及自身定义的任意字段(如果有的话)。

>>> class _2dPOINT(Structure):
      _fields_=[('x',c_int),('y',c_int)]       
>>> class _3dPOINT(_2dPOINT):
      _fields_=[('z',c_int)]
>>> p1=_2dPOINT(1,2)
>>> p2=_3dPOINT(1,2,3)
>>> print(p2.x,p2.y,p2.z)
1 2 3
>>>

2.3.2 _pack_

_pack_:一个可选的类属性,其值为小整数,指定structure实例字段对齐方式(覆盖原来对齐方式)。在赋值_fields_之前必须已定义_pack_,否则将无效(仅针对先定义structure后定义_fields_的情况)。

>>> class POINT(Structure):
      _fields_ = [("x", c_int),
                 ("xbit",c_int64,33),
                 ("y", c_int),
                 ("ybit",c_int64,33),
                 ("z",c_longlong)]
      _pack_=4    
>>> POINT.x

>>> POINT.xbit

>>> POINT.y

>>> POINT.ybit

>>> POINT.z

>>> class POINT(Structure):pass
>>> POINT._fields_ = [("x", c_int),
                 ("xbit",c_int64,33),
                 ("y", c_int),
                 ("ybit",c_int64,33),
                 ("z",c_longlong)]
>>> POINT._pack_=4
>>> POINT.x

>>> POINT.xbit

>>> POINT.y

>>> POINT.ybit

>>> POINT.z

>>>

2.3.4 _anonymous_

_anonymous_:一个可选类属性,指定未命名(anonymous)字段名称序列。在为_fields_赋值时_anonymous_必须已定义,否则将无效。该属性列出的字段类型必须是structure或union,ctypes将为其所在structure创建描述符以支持嵌套字段的直接访问而无须创建structure或union实例。

>>> class _2dPOINT(Structure):
      _fields_=[('x',c_int),('y',c_int)]       
>>> class _3dPOINT(Structure):
      _fields_=[('x',c_int),('y',c_int),('z',c_int)]
>>> class POINT(Union):
      _fields_=[('_2dp',_2dPOINT),('_3dp',_3dPOINT)]
>>> class RECT(Structure):
      _fields_=[('p1',POINT),('p2',POINT)]
      _anonymous_=['p1','p2']
>>> a=POINT((1,2),(11,22,33))
>>> a._2dp.x is a._3dp.x
True
>>> a._2dp.y is a._3dp.y
True
>>> a._2dp.z
AttributeError: '_2dPOINT' object has no attribute 'z'
>>> a._3dp.z
33
>>> p1=POINT((1,2,3))
RuntimeError: (_2dPOINT) : too many initializers
>>> p1=POINT(_3dp=(1,2,3))
>>> p2=POINT(_3dp=(4,5,6))
>>> rect=RECT(p1,p2)
>>> rect.p1._3dp.x
1
>>> rect.p1._3dp.y
2
>>> rect.p1._3dp.z
3
>>> rect.p2._3dp.x
4
>>> rect.p2._3dp.y
5
>>> rect.p2._3dp.z
6
>>> rect._3dp.x
4
>>> rect._3dp.y
5
>>> rect._3dp.z
6
>>>

_anonymous_的好处是可直接访问内部结构成员的字段而无需创建相应实例。rect.p2._3dp和rect._3dp是等效的,但是前者将创建实例p2而后者无须创建这个临时结构实例(p2)。但要注意的是,由于都是POINT类型,所以p1、p2同时被指定为_anonymous_时,在同名字段使用上产生了后者覆盖前者的问题。即对于rect._3dp,_3dp是谁的成员呢?p1还是p2?很明显答案是后者。

Structure和union实例化时既可以接受位置参数也可以接受关键字参数。位置参数按_fields_中的字段次序初始化各字段,关键字参数被解析为属性赋值,所以它们将对_fields_中的同名字段初始化,或者创建一个没有出现在_fields_的新属性。

>>> class _2dPOINT(Structure):
      _fields_=[('x',c_int),('y',c_int)]       
>>> class _3dPOINT(_2dPOINT):
      _fields_=[('z',c_int)]
>>> p=_3dPOINT(name='_3dPOINT')
>>> p.__dict__
{'name': '_3dPOINT'}
>>>

2.4 array和pointer

class ctypes.Array(*args):抽象基类,强烈建议通过“ctypes类型*正整数”这种“类型繁衍”的方式创建具体array类型。或者,也可以定义Array子类并实现类属性_length_和_type_。Array元素能用标准下标及切片读写。如果是切片读取元素,将返回一个非自身数组对象。

_length_:正整数,指定array中元素个数。访问元素时下标越界将导致IndexError异常。函数len()返回_length_。

_type_:指定义array元素类型。

Array子类在实例化时接受位置参数,按元素次序进行array初始化。

>>> IntArray5=c_int*5
>>> words=IntArray5(1,2,3)
>>> words
<__main__.c_long_Array_5 object at 0x0000022D855D99C8>
>>> for i in range(5):print(words[i],end=" ")
1 2 3 0 0
>>> class IntArray5(Array):
      _length_=5
      _type_=c_int
      def __sizeof__(self):
             print('self.__sizeof__')
             return self._length_*sizeof(self._type_)
>>> words=IntArray5(1,2,3)
>>> len(words)
5
>>> sizeof(words)
20
>>> words.__sizeof__()
self.__sizeof__
20
>>> words[:4]
[1, 2, 3, 0]
>>>

class ctypes._Pointer:私有的pointer抽象基类,具体pointer类型由以指向类型为参数的POINTER()创建,这一过程在pointer()中自动完成。

如果一个pointer对象指向array,那么数组元素可通过(pointer实例)标准下标及切片进行读写。pointer实例没有大小(元素),所以len(pointer实例)将抛出TypeError异常。负数下标将从pointer所指内存块之前读取(像c一样),而下标越界,如果幸运的话,可能会因违规访问而导致崩溃。

_type_:指定指向类型。

contents:返回pointer指向对象。为contents赋值会改变pointer指向对象。

>>> pCint=POINTER(c_int)
>>> a=c_int(90)
>>> pa=pCint(a)
>>> pa.contents.value=900
>>> a.value
900
>>> pa=pointer(a)
>>> pa.contents.value
900
>>> pa._type_

>>> pa.contents=c_int(9)
>>> a.value=90
>>> pa.contents.value
9
>>> import ctypes
>>> class pCint(ctypes._Pointer):
      _type_=c_int      
>>> a=c_int(90)
>>> pa=pCint(a)
>>> pa.contents.value
90
>>> pa.contents.value=900
>>> a
c_long(900)
>>> pa=pCint('a')
TypeError: expected c_long instead of str
>>> sizeof(pa)
8
>>> sizeof(pa.contents)
4
>>> len(pa)
TypeError: object of type 'pCint' has no len()
>>> pa[0]
900
>>> pa[900]
40
>>> pa[-1]
0
>>> pa[:3]
[900, 0, 0]
>>> pi=POINTER(c_int*5)((c_int*5)(1,2,3,4,5))
>>> pi[0][:5]
[1, 2, 3, 4, 5]
>>>

更多版本、平台、使用指南等细节,请参考:https://docs.python.org/release/3.6.5/library/ctypes.html

你可能感兴趣的:(基础学习,ctypes,ctypes,python,ctypes,c,ctypes模块,ctypes库)