std::stoi的base参数合法值为0或2–36;base=0时自动推断进制(支持0x/0X、0前缀,不支持0b),其他值按指定进制转换且不验证前缀合法性。

std::stoi 的 base 参数到底支持哪些值
std::stoi 的第三个参数 base 控制字符串按几进制解析,但它**不是任意整数都能传**。合法取值是 0 或 2–36(含)。传 1、37、-1 等会触发 std::invalid_argument 异常。
特别注意:传 0 表示“自动推断”——此时函数会跳过前导空格和可选的 +/-,然后检查是否以 0x/0X 开头(十六进制)、0 开头(八进制)或其它(十进制)。它**不会识别 0b/0B 前缀**,这是常见误解。
- 想解析
"0xFF"→ 用base = 0或base = 16 - 想解析
"0755"→ 用base = 0或base = 8(但注意:C++14 起字面量支持0b,std::stoi不支持) - 想解析
"1010"为二进制 → 必须显式传base = 2,不能依赖前缀
为什么 std::stoi("0b1010", nullptr, 2) 返回 10 而不是报错
因为 std::stoi **不验证前缀合法性**,只按指定 base 逐字符转换,遇到第一个非法字符就停。传 base = 2 时,字符集只认 '0' 和 '1',所以 "0b1010" 中的 'b' 是非法字符,转换在 'b' 处终止,实际只处理了开头的 "0" → 结果是 0?不对——等等,实测返回 10?
真相是:"0b1010" 在 base = 2 下,'0' 合法,'b' 非法 → 转换结束,结果确实是 0。如果你得到 10,说明你传的是 base = 0 或 base = 10,或者字符串其实是 "1010"(没带 0b)。务必确认原始字符串内容和传入的 base 值。
立即学习“C++免费学习笔记(深入)”;
- 调试时用
std::cout 打印原始输入 - 不要假设字符串带前缀就自动适配进制;
std::stoi没这逻辑 - 若需支持
0b前缀,得自己截掉前缀再调用,或改用std::strtol(它支持0b当base=0时识别)
std::stoi 解析失败时的错误类型和捕获方式
两种典型失败:
- 空字符串或全空白 → 抛
std::invalid_argument - 数值超出
int表示范围(如"2147483648"在 32 位系统)→ 抛std::out_of_range
必须用 try/catch 包裹,且建议分别捕获,因为处理策略不同:
try {
int x = std::stoi(s, nullptr, base);
} catch (const std::invalid_argument& e) {
// 格式错误:比如字母出现在不该出现的位置,或空串
} catch (const std::out_of_range& e) {
// 数值太大/太小:比如 "999999999999" 在 int 范围外
}别只写 catch (...) —— 你会丢失错误语义,无法区分是用户输错还是数据本身越界。
替代方案:什么时候该放弃 std::stoi 改用 std::strtol
当需要以下任一能力时,std::strtol 更合适:
- 获取**实际解析到哪个位置**(通过
endptr输出参数),例如解析混合字符串"123abc"想知道数字部分长度 - 需要支持
0b前缀(std::strtol在base = 0时识别0b/0B) - 想避免异常(返回
0或边界值并靠errno判断错误) - 目标类型是
long或long long(对应std::strtoll)
示例:安全解析带 0b 的二进制字符串
char* end;
errno = 0;
long val = std::strtol(s.c_str(), &end, 0); // 自动识别 0b、0x、0
if (errno == ERANGE || *end != '\0' || s.empty()) {
// 错误处理
}真正容易被忽略的是:即使 std::stoi 看起来简单,一旦涉及前缀、边界、错误定位,它就迅速不够用。别硬扛,该切 strtol 就切。


















