substr越界会抛出std::out_of_range异常,因标准要求pos>str.length()或pos+count>str.length()时必须报错;常见原因是直接用find返回的npos(极大值)作pos参数而未校验。

substr 为什么一越界就崩溃?
substr 不做运行时边界检查,越界直接触发 std::out_of_range 异常(debug 模式下可能直接 abort)。这不是“崩溃得不合理”,而是标准明确要求:当 pos > str.length() 或 pos + count > str.length() 且 str 非空时,抛异常。
常见诱因:
- 用
find返回的std::string::npos直接传给substr——npos是极大值(如18446744073709551615),必然越界 - 计算起始位置时没校验是否
(虽然size_t是无符号,但隐式转换后负数会绕成极大值) - 传入的
count过大,比如想取“剩余全部”却硬写1000
安全截取字符串的惯用写法
别依赖 try/catch 捕获 out_of_range——性能差、掩盖逻辑缺陷。应该在调用前主动裁剪参数:
- 起始位置
pos:用std::min(pos, str.length())保证不超长 - 截取长度
len:用std::min(len, str.length() - pos),注意先确保pos - 更简洁的写法:
str.substr(pos, std::min(len, str.size() - pos)),前提是pos已合法
示例:
立即学习“C++免费学习笔记(深入)”;
std::string s = "hello";
size_t pos = s.find("xyz"); // 返回 npos
if (pos != std::string::npos) {
auto sub = s.substr(pos, 2); // 安全
} else {
auto sub = ""; // 或 fallback 值
}npos 传给 substr 的典型错误现场
这是最常被忽略的坑:std::string::npos 是 size_t 类型的极限值,不是 -1。一旦你把它当整数用,比如 s.substr(s.find("x") + 1),npos + 1 仍是极大值,substr 必抛异常。
正确做法只有两个:
- 显式判断
!= npos后再计算偏移 - 用
std::string_view替代(C++17 起),它支持substr的宽松语义:越界时自动截断到末尾,不抛异常
例如用 string_view:
std::string_view sv = s; auto safe_sub = sv.substr(100, 10); // 返回空 view,不崩溃
substr 性能和内存注意点
substr 返回新字符串,意味着堆分配和字符拷贝。如果只是临时读取、不修改,优先用 std::string_view(C++17)或 const char* + 长度;若必须用 substr,注意:
- 不要在循环里反复
substr大字符串的开头(如逐词解析),改用find+ 偏移推进 - 移动语义对
substr返回值无效——它本来就是新建对象,不存在“移动旧字符串”的优化空间 - Release 模式下异常开销小,但避免异常仍是更可控的做法
真正难处理的不是“怎么截”,而是“怎么确定该截哪儿”——find/rfind 的返回值是否为 npos,这个判断漏掉一次,后面所有 substr 都是定时炸弹。










