You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:35:32