substr的pos为起始索引(0起),len为截取长度而非结束位置;pos>size()抛异常,pos==size()返回空串,len超限则自动截断至末尾。
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 来自用户输入或外部解析,建议封装一层带校验的工具函数substr 返回的是新分配的 std::string,有拷贝开销;而 std::string_view 的 substr 只是视图切片,零拷贝但依赖原字符串生命周期。
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 会整数回绕成极大值,直接触发越界。这个坑比参数记错更隐蔽。