substr越界会崩溃而非返回空串;pos超过s.length()即触发未定义行为,可能terminate或抛out_of_range异常;pos==s.length()合法(返空串),pos>s.length()非法;len过大自动截断,危险仅在pos越界。

substr 调用越界会直接崩溃,不是返回空串
很多同学以为 substr 遇到越界会“安全兜底”,实际在 C++ 标准里这是未定义行为(UB),常见表现是程序直接 std::terminate 或抛 std::out_of_range 异常(取决于实现和编译选项)。尤其在 Release 模式下可能静默截断或崩溃,调试时却正常——这坑特别隐蔽。
- 起始位置
pos超过字符串长度(s.length())就出事,哪怕pos == s.length()也合法(返回空串),但pos > s.length()就不行 - 长度参数
len过大没关系,substr会自动截断到末尾,真正危险的是pos - 推荐写法:
s.substr(pos, std::min(len, s.size() - pos)),但前提是先确保pos
substr 的两个重载版本行为差异很大
std::string::substr 只有两个重载:substr(size_t pos = 0) 和 substr(size_t pos, size_t len)。没有“从某字符开始到某字符结束”的版本,也没有支持负索引的变体——这点和 Python 的 s[a:b] 完全不同,硬套会错。
- 单参数版:从
pos到末尾,等价于substr(pos, npos),其中npos是std::string::npos(通常是-1的无符号值) - 双参数版:第二个参数是“要取多少个字符”,不是“截止位置”。比如
s.substr(2, 3)是取下标 2、3、4 三个字符,不是到下标 3 为止 - 如果不确定长度,别硬算
end_pos - start_pos,先检查start_pos
移动语义下 substr 返回的是新字符串,不是视图
substr 总是分配新内存拷贝数据,即使原字符串是右值或刚 move 构造来的。它不返回 std::string_view,也不共享底层缓冲区——这意味着频繁调用可能引发不必要的堆分配和拷贝开销。
- 如果只是临时查看子串内容且确定生命周期短,优先用
std::string_view(s.data() + pos, len),零拷贝 - 但注意:
string_view不拥有数据,原std::string被销毁后它就悬空,不能存成成员变量或跨函数传 - Release 编译下,
substr的拷贝成本比 debug 下更明显,尤其处理长日志或网络包时容易成为性能瓶颈
中文、UTF-8 字符串用 substr 会切碎字符
substr 按字节操作,对 UTF-8 编码的中文字符串完全无感知。一个汉字占 3 字节,若 pos 落在某个汉字中间,结果就是乱码加截断——这不是 bug,是设计如此,因为 std::string 本身不关心编码。
立即学习“C++免费学习笔记(深入)”;
- 不要用
substr做“取前 N 个汉字”或“从第 M 个汉字开始”,它只认字节偏移 - 真要按 Unicode 码点切分,得先用 ICU、utf8cpp 或手写 UTF-8 解码逻辑定位合法起点
- 最简单规避方式:确认输入是纯 ASCII,或统一转成
std::u8string+std::string_view配合 UTF-8 边界检测
事情说清了就结束











