上一篇主要从理论指导上介绍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
>>> from ctypes import *
>>>
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
>>>
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_buffer与create_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
>>>
value与raw的区别:前者所见即NUL串,后者所见即所得(数组内容)。
这两个函数是仅限windows的钩子函数,支持通过ctypes实现in-process COM服务器(DLL运行在包含自身的应用程序进程空间)。内部将调用comtypes.server.inprocserver导出的同名函数。
ctypes.util.find_library(name):查找函数库并返回其路径名。name是不带任后前缀(如lib)、后缀(如.so,.dylib)或版本号(由posix链接选项-l生成)的库名,查找失败时返回None。本函数实际功能依赖于系统。
ctypes.util.find_msvcrt():仅限windows,返回python及扩展模块使用的VC运行时库文件名。如果函数库名未定义,则返回None。
如果要释放某个扩展模块分配的内存,则使用与内存分配函数同库的内存释放函数(例如free(void*))相当重要。
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私有副本当前值。
ctypes.set_errno(value):将所在线程中系统变量errno的ctypes私有副本当前值设置为value,并返回设置前的值。
ctypes.set_last_error(value):仅限windows,将所在线程中系统变量LastError的ctypes私有副本当前值设置为value,并返回设置前的值。
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
>>>
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)
>>>
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
>>>
下面是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)
>>>
_b_base_:有些ctypes数据实例本身并不拥有内存块,而是共享基类部分内存块。只读成员_b_base_代表包含内存块的根ctypes对象。
_b_needsfree_:只读成员,当ctypes数据实例有自身内存块时为True,否则为False。
_objects:一个包含python对象的字典,或者为None,或者包含需要留存的python对象,以让内存块内容有效。该字典仅用于调试,绝对不要修改其内容。
class ctypes._SimpleCData:非公开类,所有ctypes基础数据类型基类,包含所有ctypes基础数据类型共同属性。_SimpleCData是_CData子类,继承其属性与方法。不是也不含pointer的ctypes数据类型可被序列化。
value:_SimpleCData唯一属性,包含实例实际值。对于整数和指针(pointer)类型,其值为整数;对于字符类型,其值为单字符bytes对象或单字符str对象;对于字符pointer类型,其值为python bytes对象或str对象。
当从一个ctypes实例获取(访问)value的时候,通常每次返回一个新对象。ctypes并未实施OOR(原始对象返回),总是会构造一个新对象。这对所有其他ctypes实例对象都一样。
当作为一个外部函数返回结果返回的时候,或者,比如说,在获取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。
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将创建直接通过属性访问形式以支持字段读写的描述符。
_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
>>>
_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
>>>
_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'}
>>>
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