python的array.array可通过buffer_info()获取真实内存地址,零拷贝传给c dll;需用ctypes.cast转换指针类型,确保类型码匹配(如'd'↔double)、避免gc回收、c端不越界访问。

Python array.array 如何与 C DLL 共享内存而不拷贝
直接传 array.array 给 C 函数是安全的,前提是 C 侧用 PyBuffer_GetBuffer 或等效方式访问其底层缓冲区——因为 array.array 的 buffer_info() 返回的地址是真实、连续、可读写的 C 内存地址,且生命周期由 Python 对象管理。但注意:不是所有 array 类型都支持(比如 'u' Unicode 类型不支持),只支持数值类型:'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'f', 'd'。
实操建议:
- 在 Python 中创建数组后,调用
.buffer_info()获取(address, length),address可直接转为 C 的void*(如用ctypes.cast(addr, ctypes.POINTER(ctypes.c_double))) - C DLL 函数签名应接收
double*+size_t len,不尝试自己 malloc/free - 确保 Python 对象在整个 C 调用期间不被 GC 回收(例如把
array作为参数传入时保持引用,或在 C 中用Py_INCREF若需跨线程长期持有)
ctypes.CDLL 加载后如何正确声明函数参数为 array.buffer_info() 地址
ctypes 本身不识别 array.array,必须手动提取地址并转成对应指针类型。错误做法是直接传 arr 给 argtypes 中的 ctypes.POINTER(...)——这会触发 ctypes 尝试转换,失败或产生副本。
正确流程:
- 设 C 函数为
void process_doubles(double* data, size_t n); - Python 中:
import array, ctypes arr = array.array('d', [1.0, 2.0, 3.0]) lib = ctypes.CDLL('./mylib.so') lib.process_doubles.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.c_size_t] lib.process_doubles.restype = None <h1>关键:用 buffer_info()[0] 得到地址,再 cast</h1><p>addr, _ = arr.buffer_info() ptr = ctypes.cast(addr, ctypes.POINTER(ctypes.c_double)) lib.process_doubles(ptr, len(arr)) </p> - 若 C 侧修改了数据,Python 的
arr会立即反映变更(零拷贝)
为什么用 array 而不用 list 或 numpy?
list 是 PyObject 指针数组,无法提供连续 C 数值内存;numpy.ndarray 虽然也支持,但引入依赖且默认行为更复杂(如 strides、ownership 检查)。而 array.array 是标准库、轻量、确定性内存布局,适合与简单 C DLL 快速对接。
但要注意:
-
array不支持多维——如需二维,得用一维展平 + 手动计算索引 - 类型码必须与 C 端严格匹配:
'f'↔float,'d'↔double,'i'↔int32_t(非int,因 Cint长度平台相关) - Windows 下 DLL 导出函数需加
__declspec(dllexport),且 Python 调用时函数名可能被修饰(建议用extern "C"防 name mangling)
常见段错误(SIGSEGV)的三个根源
多数 crash 发生在地址传递环节,而非算法逻辑。
- C 接收了地址但误用了长度:例如传
len(arr)却按sizeof(int)步进遍历double*,导致越界读写 - Python 数组被回收:比如把
array.array(...)写在一行里直接传参,没绑定变量名,调用前就被 GC ——arr = array.array('d', ...); lib.func(...)是安全的,lib.func(array.array('d', ...).buffer_info()[0])则危险 - 字节序或对齐问题:尤其在嵌入式或跨平台场景,
array.array('i')在小端机上生成的数据,若 C 端假设大端解析,值全错(但不会 crash,容易被忽略)
真正难调试的是第三种:数据“看起来正常”,但精度或符号错乱,此时要核对 C 类型定义和 array 类型码的二进制表示是否一致。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











