Python 3.x Python Ctypes不';当场和结构匹配时,t紧凑结构
不确定Python3.3.2总是将结构打包到新的结构单元中是否正常。下面的代码演示了这个问题 sts结构只占用8位。pkt在status之前只有24位字段,所以pkt的大小应该是32位,而不是64位。pkt的打印清楚地表明,Python将sts压缩到一个新的32位整数中,并在另一个32位整数中留下8位未使用的空间和所有其他pckt字段Python 3.x Python Ctypes不';当场和结构匹配时,t紧凑结构,python-3.x,ctypes,Python 3.x,Ctypes,不确定Python3.3.2总是将结构打包到新的结构单元中是否正常。下面的代码演示了这个问题 sts结构只占用8位。pkt在status之前只有24位字段,所以pkt的大小应该是32位,而不是64位。pkt的打印清楚地表明,Python将sts压缩到一个新的32位整数中,并在另一个32位整数中留下8位未使用的空间和所有其他pckt字段 import ctypes class sts( ctypes.BigEndianStructure ): _fields_ = [( "valid",
import ctypes
class sts( ctypes.BigEndianStructure ):
_fields_ = [( "valid", ctypes.c_uint8, 1 ),
( "inout", ctypes.c_uint8, 2 ),
( "exception", ctypes.c_uint8, 2 ),
( "error", ctypes.c_uint8, 3 ), ]
class pkt( ctypes.BigEndianStructure ):
_fields_ = [( "uid", ctypes.c_uint32, 8 ),
( "sid", ctypes.c_uint32, 16 ),
( "sts", sts, ), ]
print("sts {:d}-byte".format(ctypes.sizeof(sts)))
print("pkt {:d}-byte".format(ctypes.sizeof(pkt)))
a=pkt(0xFF,0xDEAD,(0x1,0x3,0x3,0x7))
print("uid {:02X}".format(a.uid))
print("sid {:02X}".format(a.sid))
print("sts {:02X}".format(ctypes.string_at(ctypes.addressof(a.sts))[0]))
for b in ctypes.string_at(ctypes.addressof(a),8):
print("{:02X}".format(b))
另一个帮助解释问题的代码。这段代码的输出表明Python确实以紧凑的形式打包字段,但是具有结构的字段总是从新单元开始
import ctypes
class sts( ctypes.BigEndianStructure ):
_fields_ = [( "valid", ctypes.c_uint8, 1 ),
( "inout", ctypes.c_uint8, 2 ),
( "exception", ctypes.c_uint8, 2 ),
( "error", ctypes.c_uint8, 3 ), ]
class pkt( ctypes.BigEndianStructure ):
_fields_ = [( "uid", ctypes.c_uint32, 8 ),
( "sid", ctypes.c_uint32, 16 ),
( "sts", sts ),
( "sts1", sts ),
( "gid", ctypes.c_uint16 ), ]
print("sts {:d}-byte".format(ctypes.sizeof(sts)))
print("pkt {:d}-byte".format(ctypes.sizeof(pkt)))
a=pkt(0xFF,0xDEAD,(0x1,0x3,0x3,0x7),(0x1,0x2,0x3,0x7),0xBEEFABCD)
print("uid {:02X}".format(a.uid))
print("sid {:02X}".format(a.sid))
print("sts {:02X}".format(ctypes.string_at(ctypes.addressof(a.sts))[0]))
for b in ctypes.string_at(ctypes.addressof(a),8):
print("{:02X}".format(b))
Microsoft的编译器(或带-mms位域的gcc)不会为不同的整数类型共享存储单元。但是,对于Linux上的gcc,pkt
只使用4个字节。ctypes遵循平台的约定,但只能对位字段使用整数类型。例如,您不能使用(“sts”,sts,8)
。如果编译器以4字节存储pkt
,则必须修改ctypes定义以获得相同的大小。最简单的选择是在定义中内联sts
字段。使用联合
也可以
编辑:
由于构造函数的编写方式,必须显式设置元组中的位,而元组只能与整数类型一起使用。它不会查看以前定义的结构的内容,以确定它是否可以用于扩展打开的位字段。在您的示例中,查看pkt.uid
和pkt.sid
的repr
ofs
是以字节为单位的偏移量和存储单元中的位偏移量。另一方面,对于pkt.sts
而言,ofs
的值只有4个字节——没有设置将由GETBITFIELD
和set
宏使用的位字段信息
以下是源链接:
仅供参考,在编写此编辑时,我遇到了一个错误。在非Windows平台上,
PyCField_FromDesc
将继续一个位字段,即使您切换存储大小。例如,这就是gcc在Linux上的工作方式。但我从未真正使用过它,这实际上是我第一次费心去看GETBITFIELD
宏。如果位字段切换到较小的存储大小,它将对较小的大小使用getter方法,并结合整个上下文中字段的大小信息(位偏移量和位数)。这在一般情况下是行不通的,因为宏最后以一个负数向“左”移位,这实际上是一个右移位,将所有数据移出,留下零。例如:
from ctypes import *
class Test(Structure):
_fields_ = [
('x', c_uint32, 16),
('y', c_uint8, 4),
]
>>> t = Test.from_buffer_copy(b'\xAA\xAA\xAA\xAA')
>>> bytearray(t)
bytearray(b'\xaa\xaa\xaa\xaa')
>>> hex(t.x)
'0xaaaa'
>>> t.y
0
使用pack和compact字段解决了这个问题。仅在Windows上试用
import ctypes
class sts( ctypes.BigEndianStructure ):
_pack_ = 1
_fields_ = [( "valid", ctypes.c_uint8, 1 ),
( "inout", ctypes.c_uint8, 2 ),
( "exception", ctypes.c_uint8, 2 ),
( "error", ctypes.c_uint8, 3 ), ]
class pkt( ctypes.BigEndianStructure ):
_pack_ = 1
_fields_ = [( "uid", ctypes.c_uint8 ),
( "sid", ctypes.c_uint16 ),
( "sts", sts, ), ]
print("sts {:d}-byte".format(ctypes.sizeof(sts)))
print("pkt {:d}-byte".format(ctypes.sizeof(pkt)))
a=pkt(0xFF,0xDEAD,(0x1,0x3,0x3,0x7))
print("uid {:02X}".format(a.uid))
print("sid {:02X}".format(a.sid))
print("sts {:02X}".format(ctypes.string_at(ctypes.addressof(a.sts))[0]))
for b in ctypes.string_at(ctypes.addressof(a),8):
print("{:02X}".format(b))
我的理解是,ctypes应该查看sts的本机大小,并说“嘿,它是8位的,适合32位整数的最后8位”。请参阅第二段代码,Python是针对第二个32位整数而不是第一个32位整数执行的。第一个32位和第二个32位之间的构造完全相同,只是字段顺序发生了更改。这部分对我来说没有意义。ofs实际上没有任何意义,如果我将sid和uid更改为ctypes.c_uint8和ctypes.c_uint16,而没有第三位大小,那么sid和uid的ofs将被测试。您提到的错误可能与此相同,但此错误被认为只影响windows,因此,只为windows提交了修补程序。你应该检查它是否和你在Linux上看到的一样找到答案。看起来它与操作系统的字节对齐有关。你到底想做什么?对于一般情况下的数据打包(例如网络数据包或将记录写入文件),
struct
模块要简单得多。ctypesStructure
用于调用外部函数。通常,强制与\u pack\u
进行1字节对齐只能用于匹配目标库中的类似杂注。否则,在具有不同字段大小的结构中不会得到相同的对齐方式。这不适用于网络。我试图为一组C库创建一个公共的结构“头”。否则,我需要定义这些C库使用的100多个sturct。C库希望结构以字节为单位具有可变长度的paylod,所以对齐到单个字节是可以的