substr的pos为起始索引(0起),len为截取长度而非结束位置;pos>size()抛异常,pos==size()返回空串,len超限则自动截断至末尾。

substr 的两个参数到底怎么填
substr 是 std::string 的成员函数,签名是 string substr(size_t pos = 0, size_t len = npos) const。第一个参数 pos 是起始位置(从 0 开始),第二个参数 len 是要截取的字符个数(不是结束位置)。
常见误用是把 len 当成“到第几个字符为止”,比如想取索引 2 到 5(共 4 个字符),写成 s.substr(2, 5) —— 这会取 5 个字符,越界风险陡增。
-
pos超出字符串长度(pos > size())会抛std::out_of_range -
pos == size()是合法的,返回空串;pos == size() + 1就越界了 -
len超过剩余字符数时,substr自动截断到末尾,不会报错(这是安全设计)
越界时怎么避免程序崩溃
不能依赖“运气”——哪怕你认为 pos 不会超,运行时输入不可控,必须主动防御。
- 检查
pos:用if (pos ,注意是(因为pos == s.size()合法) - 不推荐裸调
try/catch捕获std::out_of_range,开销大且掩盖逻辑问题 - 更稳妥的做法是先算安全范围:
size_t safe_pos = std::min(pos, s.size());,再传入substr - 如果业务上
pos来自用户输入或外部解析,建议封装一层带校验的工具函数
和 C 风格字符串、std::string_view 的行为差异
substr 返回的是新分配的 std::string,有拷贝开销;而 std::string_view 的 substr 只是视图切片,零拷贝但依赖原字符串生命周期。
立即学习“C++免费学习笔记(深入)”;
- C 风格(
strncpy等)需手动管理内存和终止符,容易缓冲区溢出,不推荐在 C++ 中用于子串提取 -
std::string_view::substr同样接受pos和len,但越界时行为是未定义(UB),不是抛异常 —— 必须确保调用前已校验 - 若只读且性能敏感(如解析日志、协议字段),优先用
string_view::substr并配合assert或预检
一个防越界的实用封装示例
直接用原生 substr 容易漏判,下面这个小函数把边界逻辑收拢了:
std::string safe_substr(const std::string& s, size_t pos, size_t len = std::string::npos) {
if (pos >= s.size()) return "";
len = std::min(len, s.size() - pos);
return s.substr(pos, len);
}它处理了三种典型越界场景:pos 超长、len 过大、两者叠加。注意返回空串而不是抛异常——是否该报错,取决于你的业务语义,这里默认“取不到就为空”。
真正容易被忽略的是:当 pos 来自另一个子串的查找结果(比如 s.find("key") + offset),必须确认 find 返回的不是 npos,否则 npos + offset 会整数回绕成极大值,直接触发越界。这个坑比参数记错更隐蔽。










