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

如何在Python中为C结构体动态数组分配内存?

解决ctypes调用C代码时动态结构体数组的内存分配问题

你遇到的ValueError: NULL pointer access本质是C代码中Bar结构体的f指针指向了NULL,C函数遍历数组时访问了无效内存。核心解决思路是在Python侧为结构体数组提前分配内存,或让C侧负责内存分配后在Python侧正确解析并释放内存,以下分两种场景给出具体实现:


场景1:已知结构体数组大小(静态确定)

假设你的C代码如下:

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Foo;

typedef struct {
    Foo* f;
    int count;
} Bar;

void iterate_bar(Bar* bar) {
    for (int i = 0; i < bar->count; i++) {
        printf("Foo %d: x=%d, y=%d\n", i, bar->f[i].x, bar->f[i].y);
    }
}

Python侧可以直接创建指定大小的结构体数组,再将指针传递给Bar:

import ctypes

# 映射C结构体到Python
class Foo(ctypes.Structure):
    _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]

class Bar(ctypes.Structure):
    _fields_ = [("f", ctypes.POINTER(Foo)), ("count", ctypes.c_int)]

# 加载编译好的动态库(Windows用.dll,Linux/macOS用.so)
lib = ctypes.CDLL("./libtest.so")
# 声明函数参数和返回值类型(必须,避免类型转换错误)
lib.iterate_bar.argtypes = [ctypes.POINTER(Bar)]
lib.iterate_bar.restype = None

# 分配3个Foo的数组
count = 3
foo_array = (Foo * count)()

# 给数组元素赋值
foo_array[0].x = 10
foo_array[0].y = 20
foo_array[1].x = 30
foo_array[1].y = 40
foo_array[2].x = 50
foo_array[2].y = 60

# 组装Bar结构体并调用C函数
bar = Bar()
bar.f = ctypes.cast(foo_array, ctypes.POINTER(Foo))
bar.count = count

lib.iterate_bar(ctypes.byref(bar))

场景2:数组大小动态不确定(由C侧分配内存)

如果数组大小需要C函数计算后动态分配,需让C提供内存创建和释放的接口,避免Python侧无法管理C堆内存导致泄漏:

修改后的C代码:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int x;
    int y;
} Foo;

typedef struct {
    Foo* f;
    int count;
} Bar;

// 创建并初始化Bar及内部Foo数组
Bar* create_bar(int count) {
    Bar* bar = (Bar*)malloc(sizeof(Bar));
    bar->count = count;
    bar->f = (Foo*)malloc(count * sizeof(Foo));
    
    // 示例赋值
    for (int i = 0; i < count; i++) {
        bar->f[i].x = i * 10;
        bar->f[i].y = i * 20;
    }
    return bar;
}

void iterate_bar(Bar* bar) {
    for (int i = 0; i < bar->count; i++) {
        printf("Foo %d: x=%d, y=%d\n", i, bar->f[i].x, bar->f[i].y);
    }
}

// 释放C侧分配的内存
void free_bar(Bar* bar) {
    free(bar->f);
    free(bar);
}

Python侧调用代码:

import ctypes

class Foo(ctypes.Structure):
    _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]

class Bar(ctypes.Structure):
    _fields_ = [("f", ctypes.POINTER(Foo)), ("count", ctypes.c_int)]

lib = ctypes.CDLL("./libtest.so")

# 声明所有C函数的类型
lib.create_bar.argtypes = [ctypes.c_int]
lib.create_bar.restype = ctypes.POINTER(Bar)

lib.iterate_bar.argtypes = [ctypes.POINTER(Bar)]
lib.iterate_bar.restype = None

lib.free_bar.argtypes = [ctypes.POINTER(Bar)]
lib.free_bar.restype = None

# 调用C函数创建Bar
bar_ptr = lib.create_bar(4)

# 访问结构体内容
print(f"数组长度: {bar_ptr.contents.count}")
for i in range(bar_ptr.contents.count):
    foo = bar_ptr.contents.f[i]
    print(f"Foo {i}: x={foo.x}, y={foo.y}")

# 调用遍历函数
lib.iterate_bar(bar_ptr)

# 必须调用C的释放函数,Python无法回收C堆内存
lib.free_bar(bar_ptr)

关键注意事项

  • 必须显式声明C函数的argtypes和restype,否则ctypes会自动进行不安全的类型转换,引发未知错误。
  • Python侧分配的数组,只要对应的Python对象(如foo_array)未被垃圾回收,内存就有效;若需长期使用,需保证对象存活。
  • C侧分配的内存,必须用C提供的释放函数清理,不能用Python的del,否则会造成内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:40:02