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

如何在Python中使用ctypes调整可变大小缓冲区的尺寸?

问题:ctypes可变大小结构体修改尺寸无效的解决方法

问题代码

import struct
import ctypes as ct

def read_buffer(data):
    size = struct.unpack('<h', data[64:66])[0]
    class TableData(ct.Structure):
        _fields_ = [('delta', ct.c_float),
                    ('x', ct.c_float)]
        def __repr__(self):
            return f'TableData(delta={self.delta}, x={self.x})'

    class TableParameters(ct.Structure):
        _fields_ = [('parameter1', ct.c_uint16 * 30),
                    ('parameter2', ct.c_float),
                    ('size', ct.c_uint16),
                    ('parameter3', ct.c_uint16),
                    ('parameter4', ct.c_float),
                    ('parameter5', ct.c_float),
                    ('Table', TableData * size)]
        def __repr__(self):
            return (f'TableParameters(parameter1={list(self.parameter1)},'
                    f'parameter2={self.parameter2},'
                    f'size={self.size},'
                    f'parameter3={self.parameter3},'
                    f'parameter4={self.parameter4},'
                    f'parameter5={self.parameter5},'
                    f'Table={list(self.Table)}')

    return TableParameters.from_buffer_copy(data)

data = struct.pack('<30HfHHff4f',*range(30),1.125,2,8,2.5,3.25,4.75,5.5,6.25,7.75)
a = read_buffer(data)
print (a.parameter1[0])
print (a.parameter1[3])
print (a.parameter5)
print ('------------')
print (len(a.Table))
a.size = 10
print (len(a.Table))

运行输出

0
3
3.25        
------------
2
2

问题分析

当前代码中,TableParameters结构体的Table字段类型是TableData * size,这里的size是函数调用时从数据中解析出的静态值,结构体类在定义时就固定了Table的数组大小。修改实例的size字段只是改变了结构体中的一个数值,并不会改变已定义的数组类型的内存布局和大小,因此len(a.Table)始终是定义时的初始值。

解决方案

方案1:动态重建结构体类(适合修改尺寸后重新加载数据的场景)

将结构体类的创建逻辑抽离成单独函数,当需要修改size时,根据新尺寸重新创建结构体类,再将原有数据复制到新实例中:

import struct
import ctypes as ct

def create_table_params_class(size):
    class TableData(ct.Structure):
        _fields_ = [('delta', ct.c_float),
                    ('x', ct.c_float)]
        def __repr__(self):
            return f'TableData(delta={self.delta}, x={self.x})'

    class TableParameters(ct.Structure):
        _fields_ = [('parameter1', ct.c_uint16 * 30),
                    ('parameter2', ct.c_float),
                    ('size', ct.c_uint16),
                    ('parameter3', ct.c_uint16),
                    ('parameter4', ct.c_float),
                    ('parameter5', ct.c_float),
                    ('Table', TableData * size)]
        def __repr__(self):
            return (f'TableParameters(parameter1={list(self.parameter1)},'
                    f'parameter2={self.parameter2},'
                    f'size={self.size},'
                    f'parameter3={self.parameter3},'
                    f'parameter4={self.parameter4},'
                    f'parameter5={self.parameter5},'
                    f'Table={list(self.Table)}')
    return TableParameters

def read_buffer(data):
    size = struct.unpack('<h', data[64:66])[0]
    TableParamsClass = create_table_params_class(size)
    return TableParamsClass.from_buffer_copy(data)

data = struct.pack('<30HfHHff4f',*range(30),1.125,2,8,2.5,3.25,4.75,5.5,6.25,7.75)
a = read_buffer(data)
print(a.parameter1[0])
print(a.parameter1[3])
print(a.parameter5)
print('------------')
print(len(a.Table))

# 修改尺寸并重建实例
new_size = 10
NewTableParamsClass = create_table_params_class(new_size)
new_a = NewTableParamsClass()
# 复制原有固定字段数据
ct.memmove(ct.addressof(new_a), ct.addressof(a), ct.sizeof(a))
new_a.size = new_size
# 初始化新的Table元素
for i in range(new_size):
    new_a.Table[i] = ct.cast((ct.c_float(0.1*i), ct.c_float(10+i)), ct.POINTER(TableData)).contents

print(len(new_a.Table))

方案2:使用指针动态管理内存(适合动态增减元素的场景)

将Table字段改为指针类型,手动分配和释放内存,实现灵活调整数组大小:

import struct
import ctypes as ct

class TableData(ct.Structure):
    _fields_ = [('delta', ct.c_float),
                ('x', ct.c_float)]
    def __repr__(self):
        return f'TableData(delta={self.delta}, x={self.x})'

class TableParameters(ct.Structure):
    _fields_ = [('parameter1', ct.c_uint16 * 30),
                ('parameter2', ct.c_float),
                ('size', ct.c_uint16),
                ('parameter3', ct.c_uint16),
                ('parameter4', ct.c_float),
                ('parameter5', ct.c_float),
                ('Table', ct.POINTER(TableData))]
    def __repr__(self):
        table_list = [self.Table[i] for i in range(self.size)] if self.Table else []
        return (f'TableParameters(parameter1={list(self.parameter1)},'
                f'parameter2={self.parameter2},'
                f'size={self.size},'
                f'parameter3={self.parameter3},'
                f'parameter4={self.parameter4},'
                f'parameter5={self.parameter5},'
                f'Table={table_list}')

def read_buffer(data):
    base_size = ct.sizeof(TableParameters) - ct.sizeof(ct.POINTER(TableData))
    size = struct.unpack('<h', data[64:66])[0]
    params = TableParameters()
    # 复制固定部分数据
    ct.memmove(ct.addressof(params), data, base_size)
    # 分配Table内存并复制数据
    params.Table = ct.cast(ct.malloc(size * ct.sizeof(TableData)), ct.POINTER(TableData))
    ct.memmove(params.Table, data[base_size:], size * ct.sizeof(TableData))
    return params

data = struct.pack('<30HfHHff4f',*range(30),1.125,2,8,2.5,3.25,4.75,5.5,6.25,7.75)
a = read_buffer(data)
print(a.parameter1[0])
print(a.parameter1[3])
print(a.parameter5)
print('------------')
print(a.size)

# 修改尺寸并重新分配内存
new_size = 10
# 释放原有内存
ct.free(a.Table)
# 分配新内存
a.Table = ct.cast(ct.malloc(new_size * ct.sizeof(TableData)), ct.POINTER(TableData))
a.size = new_size
# 初始化新元素
for i in range(new_size):
    a.Table[i].delta = 0.1 * i
    a.Table[i].x = 10 + i

print(a.size)
print(len([a.Table[i] for i in range(a.size)]))
# 最后记得释放内存
ct.free(a.Table)

注意事项

  • 方案2需要手动管理内存,ct.malloc分配的内存必须用ct.free释放,避免内存泄漏。
  • 动态重建类的方式更贴近原有代码逻辑,但每次修改尺寸都需重新创建实例;指针方式灵活性更高,但需要额外处理内存操作。

内容的提问来源于stack exchange,提问作者Isaac Zaiek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:45:13