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

使用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

疑问

  1. 我是否正确打印了地址?还是pybind11确实创建了Python字符串的副本?
  2. 若操作有误,如何实现零拷贝传递?

问题解答

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 03:31:25