空指针传入std::string(const char*)必崩溃,因调用strlen(nullptr)属未定义行为;必须显式判空,且需注意源内存生命周期与嵌入\0时须用带长度构造函数。

const char* 为空指针时会崩溃
直接用 std::string(const char*) 构造函数传入空指针是未定义行为,多数实现会立即 crash(比如调用 strlen(nullptr))。这不是“偶尔出错”,而是只要触发就必崩。
- 永远不要写
std::string s(p),除非你能 100% 确保p非空 - 常见陷阱:C API 返回可能为
nullptr的字符串(如getenv()、sqlite3_column_text()) - 安全做法是显式判空:
const char* p = get_c_string(); std::string s = p ? std::string(p) : std::string();
const char* 指向栈/局部缓冲区需注意生命周期
std::string 构造时会复制内容,不依赖原指针后续是否有效——这点是安全的。但容易误以为“构造完就万事大吉”,而忽略源头数据提前销毁的问题。
- 错误示例:
std::string bad() { char buf[64]; snprintf(buf, sizeof(buf), "hello"); return std::string(buf); // ✅ 复制了,buf 销毁不影响 } std::string good() { static char buf[64]; // 或全局/堆内存 snprintf(buf, sizeof(buf), "hello"); return std::string(buf); // 更稳妥,但非必须 } - 真正风险在于:你传给
std::string的指针本身指向已释放内存(比如函数返回后栈帧弹出、free()后继续用),构造动作本身不救你 - 结论:构造安全 ≠ 源数据安全;检查的是指针值有效性,不是内存归属
含嵌入空字符(\0)时要用带长度的构造函数
默认 std::string(const char*) 遇到第一个 <p>默认 <code>std::string(const char*) 遇到第一个 \0 就停,会截断。如果源数据是二进制或含中间 \0,必须显式传长度。
<p>默认 <code>std::string(const char*) 遇到第一个 \0 就停,会截断。如果源数据是二进制或含中间 \0,必须显式传长度。,必须显式传长度。
- 错误:
const char data[] = {'h','e','\0','l','o'}; std::string s(data); // 得到 "he",不是 "he\0lo" - 正确:
std::string s(data, sizeof(data)); // 显式长度,保留全部字节
- 长度必须准确:多传会越界读,少传丢数据;推荐配合
sizeof或明确 length 变量 - 注意:
std::string允许内部含\0,所有成员函数(如size()、data())都按实际长度处理
性能敏感场景下避免多余拷贝
构造 std::string 本质是分配内存 + 复制字节。对短字符串(通常 ≤ 15–22 字节,取决于 STL 实现),小字符串优化(SSO)能避免堆分配,但复制仍发生。
立即学习“C++免费学习笔记(深入)”;
- 如果只是临时使用、且确定生命周期可控,考虑用
std::string_view替代:const char* p = ...; if (p) { std::string_view sv(p); use(sv); // 不拷贝,不管理内存 } - 若必须转
std::string(例如要修改、拼接、存入容器),无法绕过拷贝;此时重点是确保源指针有效、长度正确 - 不要试图“复用”已有
std::string对象来避免构造(如s.assign(p)),语义等价且无实质优势
最常被忽略的其实是第一点:空指针检查不是“防御性编程可选”,而是硬性前提。其他问题在代码审查时容易发现,但 nullptr 崩溃往往只在特定环境暴露,修起来又快又痛。


















