不能用 &str[0] 因为空字符串时是未定义行为,而 str.data() 在 C++11 起保证返回合法指针且零开销;data() 不保证 '\0' 结尾,c_str() 保证;可写缓冲区应优先用 vector,C++20 可用 span。

直接用 &str[0] 不安全,str.data() 才是正确且零开销的方案。
为什么不能用 &str[0]?
当 std::string 为空时,&str[0] 是未定义行为(UB),哪怕在多数编译器上“碰巧”返回了合法地址,C++ 标准也明确禁止访问空字符串的首元素。而 str.data() 在 C++11 起保证返回有效指针(指向内部缓冲区,即使为空也合法),且不触发拷贝。
-
str.data()返回const char*(C++11–17)或const char*/char*(C++20,取决于 const 性) -
&str[0]等价于&*(str.begin()),空串时str.begin() == str.end(),解引用无效 - 某些 STL 实现(如 libstdc++ 的 debug mode)会在空串下触发断言失败
data() 和 c_str() 有什么区别?
两者都返回 const char*,但语义和使用边界不同:
-
str.data():返回底层字节缓冲区起始地址,**不保证以'\0'结尾**(虽然通常就是,但标准不强制) -
str.c_str():返回以'\0'结尾的 C 风格字符串,**必须保证末尾有 null 终止符**,调用可能触发内部 null 插入(极少数实现对短字符串优化例外,但行为一致) - 若你只传给需要原始字节流 + 显式长度的 API(如
send()、write()、GPU buffer upload),用data()更准确;若传给printf、open()、fopen()这类期待 null-terminated 的函数,必须用c_str()
如何安全获取可写原生缓冲区(C++17+)?
如果目标 API 需要非 const 字节指针(例如 recv() 填充数据、加密库 in/out buffer),std::string 默认不提供可写视图。此时不能强转 const_cast<char>(str.data())</char> —— 这会破坏 const 正确性,且若 string 使用 COW 或 small-string 优化,可能引发未定义行为。
立即学习“C++免费学习笔记(深入)”;
- 推荐做法:用
std::vector<char></char>替代,它天然支持&vec[0](非空时)或vec.data(),且生命周期可控 - 若必须用
std::string,先确保容量足够:str.resize(n);,再用const_cast<char>(str.data())</char>—— 仅当你知道该 string 没被其他 const 引用共享,且没开启 COW(现代 libc++/MSVC 已弃用 COW)才勉强可行 - C++20 提供
std::span<char></char>,可配合std::vector构造安全可写视图,比裸指针更清晰
常见误用场景与性能提醒
很多人以为 data() 会复制或分配内存 —— 完全不会。它是纯指针取值,汇编级就是一条 lea 或 mov 指令。但要注意这些隐性成本:
- 频繁调用
str.data()本身无开销,但若每次调用前都发生str += ...或str.append(),可能导致多次 realloc,缓冲区地址变更 - 把
str.data()存成裸指针长期持有很危险:一旦 string 被移动、销毁、重分配,指针立即悬空 - 跨线程传递
data()指针前,必须确保 string 对象生命周期覆盖整个使用期,且无并发修改
最易被忽略的一点:data() 返回的指针有效性**完全依赖 string 对象存活且未被移动**,不是“只要 string 还在作用域里就安全”—— 移动构造/赋值后原对象处于有效但未指定状态,其 data() 不再可用。


















