Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3.x Python Ctypes不';当场和结构匹配时,t紧凑结构_Python 3.x_Ctypes - Fatal编程技术网

Python 3.x Python Ctypes不';当场和结构匹配时,t紧凑结构

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",

不确定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", 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
宏使用的位字段信息

以下是源链接:

由于gcc(Windows上除外)会自动尝试打包数据以填充位字段存储单元(但在1字节边界上,即它不会将28位字段与另一个结构中定义的4位字段组合),因此在使用位字段时请小心仔细检查布局。要调整ctypes定义,可以内联字段、显式设置整数类型的位数或使用并集


仅供参考,在编写此编辑时,我遇到了一个错误。在非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
模块要简单得多。ctypes
Structure
用于调用外部函数。通常,强制与
\u pack\u
进行1字节对齐只能用于匹配目标库中的类似杂注。否则,在具有不同字段大小的结构中不会得到相同的对齐方式。这不适用于网络。我试图为一组C库创建一个公共的结构“头”。否则,我需要定义这些C库使用的100多个sturct。C库希望结构以字节为单位具有可变长度的paylod,所以对齐到单个字节是可以的