繁体   English   中英

Python 3.1.3 ctypes.structure无法正确地对位进行排序,并且意外地修改了数据

[英]Python 3.1.3 ctypes.structure does not order bits properly and unexpectedly modifies data

我定义了以下结构

import ctypes
from ctypes import *
class r( BigEndianStructure ):
    _fields_ = [( "d0", c_uint32, 28 ),
                ( "d1", c_uint32, 18 ),
                ( "d2", c_uint32, 18 ),
                ( "d3", c_uint16, 16 ),
                ( "d4", c_uint16, 16 ), ]

然后用以下代码进行测试

a = r(0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDD,0xEEEE)
for byte in string_at( addressof( a ), sizeof( a ) ):
    print(hex(byte),end="")

结果是

0xaa 0xaa 0xaa 0xa0 0xee 0xee 0xc0 0x0 0x33 0x33 0x0 0x0 0xdd 0xdd 0xee 0xee

预期的结果是

0xaa 0xaa 0xaa 0xa0 0xbb 0xbb 0xc0 0x0 0xcc 0xcc 0xc0 0x0 0xdd 0xdd 0xee 0xee

不仅结构未压缩,结果数据与输入的数据不同。 我是否犯了任何错误或Python喜欢以自己的想法修改数据?

字段左对齐(填充到右侧)

栏位d0-

(AAAA AAAA & FFFFFFF (28 bits)) << (32 - 28 = 4) = AAAA AAA0

栏位d1-

(BBBB BBBB & 3FFFF (18 bits)) << (32 - 18 = 14)  = EEEE C000

栏位d2-

(CCCC CCCC & 3FFFF (18 bits)) << (32 - 18 = 14) = 3333 0000

字段d1和d2都不能同时容纳在32位字段中-因此d2在下一个32位时隙中对齐。

字段d1的说明性分步示例:

BBBB BBBB & 3FFFF (only least-significant 18 bits kept) = 3BBBB

3BBBB << 14 (pad the last 14 bits) = EEEE C000

看起来问题出在将18位宽度的值存储为32位,然后将其解释为完整的32位值。

让我们看看0xBBBBBBBB发生了0xBBBBBBBB

0xBBBBBBBB = 10111011101110111011101110111011b
0xBBBBBBBB & 3FFFF (bit width of 18) = 111011101110111011b

如果您的结构看起来像| d0 | 4 pad bits | d1 | 14 pad bits | d2 | 14 pad bits ... | | d0 | 4 pad bits | d1 | 14 pad bits | d2 | 14 pad bits ... | 那么将d1的地址解释为32位值将是:

11101110111011101100000000000000b = 0xEEEEC000

基本上,当像这样读取内存时,不是获得期望值的18位掩码,而是获得14位移位。

使用适合容器类型的位字段以避免对齐填充。 在下面的示例中,4 + 8 + 16适合c_uint32,但4 + 8 + 16 + 5不适合,因此d3与下一个c_uint32对齐:

from ctypes import *
class r( BigEndianStructure ):
    _fields_ = [('d0',c_uint32, 4),
                ('d1',c_uint32, 8),
                ('d2',c_uint32,16),
                ('d3',c_uint32, 5)]

def fld(n):
    return '[' + '-'*(n-2) + ']'

def pad(n):
    return '.'*n

print(fld(4),fld(8),fld(16),pad(4),fld(5),pad(27),sep='')

for i in range(1,17):
    v = 2**i-1
    a = r(v,v,v,v)
    for byte in string_at( addressof( a ), sizeof( a ) ):
        print('{0:08b}'.format(byte),end='',sep='')
    print()

输出量

二进制输出使数字更易于可视化。 请注意,5位字段无法容纳在第一个c_uint32的其余4位中,因此添加了4位填充以在下一个c_uint32中开始5位字段。

[--][------][--------------]....[---]...........................
0001000000010000000000000001000000001000000000000000000000000000
0011000000110000000000000011000000011000000000000000000000000000
0111000001110000000000000111000000111000000000000000000000000000
1111000011110000000000001111000001111000000000000000000000000000
1111000111110000000000011111000011111000000000000000000000000000
1111001111110000000000111111000011111000000000000000000000000000
1111011111110000000001111111000011111000000000000000000000000000
1111111111110000000011111111000011111000000000000000000000000000
1111111111110000000111111111000011111000000000000000000000000000
1111111111110000001111111111000011111000000000000000000000000000
1111111111110000011111111111000011111000000000000000000000000000
1111111111110000111111111111000011111000000000000000000000000000
1111111111110001111111111111000011111000000000000000000000000000
1111111111110011111111111111000011111000000000000000000000000000
1111111111110111111111111111000011111000000000000000000000000000
1111111111111111111111111111000011111000000000000000000000000000

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM