如何让Python ctypes的24位结构体不自动对齐(占用3字节)?
使用ctypes定义24位位域结构体时的填充问题
我用ctypes实现结构体的Python表示,当位域结构体大小≥4字节时一切正常,但用位域语法定义单个24位(3字节)字段时,会被自动添加1字节填充;而用数组定义相同长度的字段则不会出现这个问题,代码和输出如下:
from ctypes import * class MyStructure(BigEndianStructure): _fields_ = [ ("vol", c_uint32, 24) ] _pack_ = 1 class MyStructure2(BigEndianStructure): _fields_ = [ ("vol", c_uint8 * 3) ] _pack_ = 1 c = MyStructure() c.vol = 0xABCDEF print(bytes(c)) # 期望是24位 print(len(bytes(c))) # 期望长度为3 d = MyStructure2() d.vol[0] = 0xAB d.vol[1] = 0xCD d.vol[2] = 0xEF print(bytes(d)) # 确实是24位 print(len(bytes(d))) # 长度为3
输出结果:
$ python test.py b'\xab\xcd\xef\x00' 4 b'\xab\xcd\xef' 3
请问应如何修改第一个结构体使其大小为3字节?
解决方案
ctypes的位域是基于底层整数类型进行对齐的,你用c_uint32作为位域的基础类型时,哪怕只用到24位,它依然会按照4字节的c_uint32来对齐,因此自动补充了1字节填充。要实现3字节的位域结构体,可以尝试以下几种方法:
方法1:拆分位域为多个单字节位域
将24位拆分为3个8位的位域,分别对应每个字节,这样结构体大小会严格为3字节:
from ctypes import * class MyStructure(BigEndianStructure): _fields_ = [ ("vol_high", c_uint8, 8), # 对应0xAB ("vol_mid", c_uint8, 8), # 对应0xCD ("vol_low", c_uint8, 8) # 对应0xEF ] _pack_ = 1 # 使用时可以手动组合成24位数值 c = MyStructure() c.vol_high = 0xAB c.vol_mid = 0xCD c.vol_low = 0xEF print(bytes(c)) # 输出 b'\xab\xcd\xef' print(len(bytes(c))) # 输出 3
方法2:强制指定结构体大小(兼容性需注意)
通过重写_sizeof_方法,强制让ctypes认为结构体大小是3字节。这种方式依赖ctypes内部实现,不同平台可能存在兼容性问题:
from ctypes import * class MyStructure(BigEndianStructure): _fields_ = [ ("vol", c_uint32, 24) ] _pack_ = 1 def _sizeof_(self): return 3 c = MyStructure() c.vol = 0xABCDEF print(bytes(c)) # 输出 b'\xab\xcd\xef' print(len(bytes(c))) # 输出 3
方法3:封装自定义24位类型(更优雅)
如果需要直接操作完整的24位数值,可以封装一个自定义类型,内部用3字节数组存储,对外提供统一的属性访问接口:
from ctypes import * class Uint24: def __init__(self): self._bytes = c_uint8 * 3() @property def value(self): # 从3字节组合成24位整数 return (self._bytes[0] << 16) | (self._bytes[1] << 8) | self._bytes[2] @value.setter def value(self, val): # 将24位整数拆分到3字节中 self._bytes[0] = (val >> 16) & 0xFF self._bytes[1] = (val >> 8) & 0xFF self._bytes[2] = val & 0xFF class MyStructure(BigEndianStructure): _fields_ = [ ("vol", Uint24) ] _pack_ = 1 c = MyStructure() c.vol.value = 0xABCDEF print(bytes(c.vol._bytes)) # 输出 b'\xab\xcd\xef' print(len(bytes(c.vol._bytes))) # 输出 3
内容的提问来源于stack exchange,提问作者Alessandro
相关产品推荐
相关产品推荐

