Pybind11如何将C++三维数组/数组指针无拷贝返回为Numpy数组
实现方案(无拷贝返回C++三维数组到Numpy)
核心原理是通过Pybind11提供的py::array_t类型直接封装C++侧的内存指针,正确配置形状、步长和内存所有权策略后,即可实现零拷贝映射为Python侧的Numpy数组。
1. 前置依赖与声明
首先在C++代码中引入必要头文件,简化命名空间调用:
#include <pybind11/pybind11.h> #include <pybind11/numpy.h> namespace py = pybind11;
注意:如果要返回的C++数组是栈上分配的局部变量(例如函数内定义int arr[100][100][3]),绝对不能直接返回指针,函数退出后栈内存会被回收,Python侧访问会触发内存错误。栈上数组只能做拷贝返回,要实现零拷贝需要将数组改为静态存储、全局存储或者堆分配,保证返回Python后内存仍然有效。
2. 零拷贝返回实现示例
示例1:返回静态存储的三维数组
适合生命周期和程序运行周期一致的数组场景:
py::array_t<int> get_static_3d_array() { // 静态存储,程序运行期间内存不会释放 static int arr[100][100][3]; // 此处可添加arr的赋值、修改逻辑 // 定义数组形状,三个维度分别为100、100、3 const std::array<ssize_t, 3> shape = {100, 100, 3}; // 定义每个维度的步长,单位为字节,int占4字节所以乘4 const std::array<ssize_t, 3> strides = { 100 * 3 * sizeof(int), 3 * sizeof(int), sizeof(int) }; // 静态数组不需要额外释放,传入空capsule即可 return py::array_t<int>( shape, strides, &arr[0][0][0], py::capsule() ); }
示例2:返回堆分配的三维数组,自动管理内存
适合动态生成的数组场景,会在Python侧Numpy数组被垃圾回收时自动释放C++侧内存,避免泄漏:
py::array_t<int> get_heap_3d_array() { // 堆分配三维数组 int (*arr)[100][3] = new int[100][100][3]; // 此处可添加arr的赋值、修改逻辑 const std::array<ssize_t, 3> shape = {100, 100, 3}; const std::array<ssize_t, 3> strides = { 100 * 3 * sizeof(int), 3 * sizeof(int), sizeof(int) }; // 自定义释放器,Python侧数组销毁时自动调用delete释放堆内存 py::capsule deleter(arr, [](void *p) { delete[] static_cast<int(*)[100][3]>(p); }); return py::array_t<int>( shape, strides, &arr[0][0][0], deleter ); }
3. 绑定到Pybind11模块
将函数绑定到你的模块即可在Python侧调用:
PYBIND11_MODULE(your_module_name, m) { m.def("get_static_3d_array", &get_static_3d_array, "Get static 3D int array as numpy array (zero copy)"); m.def("get_heap_3d_array", &get_heap_3d_array, "Get heap allocated 3D int array as numpy array (zero copy)"); }
4. Python侧调用验证
import your_module_name import numpy as np arr = your_module_name.get_heap_3d_array() print(arr.shape) # 输出 (100, 100, 3) print(arr.dtype) # 输出 int32,和C++侧int类型对应 # 验证零拷贝:修改Python侧数组的值,C++侧对应内存会同步更新,反之亦然
关键注意事项
- 如果你的C++数组是非连续存储的,只需要对应调整
strides参数的数值即可正常映射 - 如果确实需要返回栈上的局部数组,去掉
py::array_t构造参数里的capsule即可,Pybind11会自动做内存拷贝返回 - 零拷贝场景下不要在C++侧提前释放已经返回给Python的数组内存,否则会触发Python侧崩溃
内容的提问来源于stack exchange,提问作者Krishna Teja Nuthalapati
相关产品推荐
相关产品推荐

