使用pybind11在Python与C++间传递字符串/bytes是否会产生副本?
关于pybind11字符串零拷贝传递的疑问
我尝试用pybind11将Python作为前端绑定C的重型计算逻辑,需要在Python与C之间传递字符串/bytes。查阅pybind11官方文档后,以为“无转换”意味着双方会指向同一块内存数据,但测试发现地址不同。
测试代码与输出
C++代码
void string_fn(std::string& x) { std::cout << "c++ value is: " << x << std::endl; std::cout << "c++ address is: " << &x << std::endl; } PYBIND11_MODULE(module_name, module_handle) { module_handle.def("string_fn", &string_fn, py::return_value_policy::reference); }
Python代码
a = "test_string" # 与a = b"test_string"输出一致 print("python address: ", hex(id(a))) # 调用C++函数 string_fn(a)
输出结果
python address: 0x7f268c65f240 c++ value is: test_string c++ address is: 0x7ffff24c1830
疑问
- 我是否正确打印了地址?还是pybind11确实创建了Python字符串的副本?
- 若操作有误,如何实现零拷贝传递?
问题解答
1. 地址打印与副本问题
你打印的地址是正确的,但这里确实产生了副本,原因如下:
- Python字符串(含bytes)是不可变对象,内存布局和C++
std::string完全不同。pybind11为了让你能用std::string接口访问,会创建临时的std::string副本,你打印的正是这个副本的地址,而非Python原对象的内存地址。 - 你设置的
py::return_value_policy::reference无意义,因为你的函数是void返回,该参数仅控制返回值的内存策略,和入参无关。
2. 实现零拷贝传递的方法
要实现零拷贝,需直接访问Python对象的底层内存,避免转换成std::string:
方法一:处理Python bytes对象(推荐)
针对Python的bytes类型,用py::bytes作为C++参数,通过py::buffer_info获取底层指针:
void bytes_fn(py::bytes x) { auto buffer = py::buffer_info(x); std::cout << "c++ buffer address: " << buffer.ptr << std::endl; // 直接访问底层内存,长度为buffer.size const char* data = static_cast<const char*>(buffer.ptr); std::cout << "value: " << std::string(data, buffer.size) << std::endl; } PYBIND11_MODULE(module_name, module_handle) { module_handle.def("bytes_fn", &bytes_fn); }
Python侧调用:
a = b"test_string" print("python address: ", hex(id(a))) bytes_fn(a)
此时buffer.ptr会与Python bytes对象的底层数据地址一致,实现零拷贝。
方法二:处理Python str对象
如果是Python的Unicode字符串(str),可通过py::str结合py::buffer_info直接访问底层内存(注意Python 3.3+默认用UTF-8编码存储):
void str_fn(py::str x) { auto buffer = py::buffer_info(x); std::cout << "c++ buffer address: " << buffer.ptr << std::endl; const char* data = static_cast<const char*>(buffer.ptr); std::cout << "value: " << std::string(data, buffer.size) << std::endl; }
注意:Python str是不可变对象,C++侧只能只读访问,禁止修改,否则会破坏Python内存安全。
方法三:基于std::string的零拷贝视图
若必须用std::string类型,可构造不拷贝数据的std::string视图,但需确保Python对象在C++使用期间不被垃圾回收:
void no_copy_string_fn(py::bytes x) { auto buffer = py::buffer_info(x); // 构造不拷贝数据的std::string std::string view(static_cast<const char*>(buffer.ptr), buffer.size, std::string::allocator_type()); std::cout << "c++ string view address: " << view.data() << std::endl; std::cout << "value: " << view << std::endl; }
Python侧需保持对bytes对象的引用,避免其被回收导致悬垂指针。
内容的提问来源于stack exchange,提问作者npsable
相关产品推荐
相关产品推荐

