推荐使用std::stoi:语法简洁,能抛出std::invalid_argument和std::out_of_range异常以区分空串、非法格式与溢出,但不检查尾部非法字符;atoi快但不安全,stringstream安全但冗长。

atoi 用起来快,但不安全
atoi 是 C 风格函数,接受 const char*,所以得先调 c_str()。它遇到非法字符就停,返回 0 —— 但 0 也可能是合法转换结果,根本分不清是“转成功了”还是“根本没转”。比如 atoi("0") 和 atoi("abc") 都返回 0,毫无提示。
常见错误现象:atoi("123abc") 返回 123(静默截断),atoi("") 或 atoi(" ") 返回 0(不报错也不提示空输入)。
实操建议:
- 只在确定输入绝对干净(如配置文件里写死的数字字符串)时用
atoi - 永远别对用户输入、网络数据、文件读取内容直接用
atoi - 如果非要兼容旧代码,至少加一层非空和首字符检查:
s.empty() || !std::isdigit(s[0]) && s[0] != '-'
stringstream 更安全,但写法略啰嗦
std::stringstream 走的是 C++ IO 流路径,会检查转换是否完全成功。关键不是“能不能转”,而是“有没有剩东西”——用 ss.fail() 判断失败,再用 ss.eof() 确认是否消费完全部字符。
立即学习“C++免费学习笔记(深入)”;
示例:
std::string s = "123";
std::stringstream ss(s);
int x;
ss >> x;
if (ss.fail() || !ss.eof()) {
// 转换失败或有残留字符,比如 "123abc" 这里会进这个分支
}使用场景:
- 需要区分“全字符串有效”和“部分有效”的逻辑(如协议解析)
- 后续还要继续从同一字符串里读其他类型(比如先读 int 再读 float)
- 项目已用大量流操作,保持风格统一
C++11 起推荐用 stoi,兼顾简洁与健壮
std::stoi 是目前最平衡的选择:语法像 atoi 一样简单,又像 stringstream 一样能抛异常、支持范围检查。
它会在以下情况抛 std::invalid_argument(空串、纯空白、非数字前缀)或 std::out_of_range(溢出):
stoi("")stoi(" ")stoi("abc")-
stoi("99999999999999999999")(超出 int 范围)
实操建议:
- 默认用
stoi,95% 场景够用 - 捕获异常比检查返回值更直观:
try { x = std::stoi(s); } catch (const std::invalid_argument&) { /* 格式错 */ } - 注意它不处理尾部垃圾字符:
stoi("123abc")仍返回 123 —— 如果需要严格匹配,得自己检查s.find_first_not_of("0123456789+-", s[0]=='-'?1:0)
性能差异其实没那么重要
单次转换耗时都在纳秒级,atoi 比 stoi 快不到 10ns,比 stringstream 快约 2–3 倍 —— 但除非你在循环里每秒转百万次,否则这点差别完全被内存访问、缓存命中率掩盖。
真正影响性能的是错误处理方式:频繁抛异常(尤其是 stoi)在某些编译器+STL 实现下开销明显;而 stringstream 构造对象本身就有固定成本。
容易被忽略的地方:所有方法都要求输入是窄字符串(std::string)。如果处理的是 std::u16string 或 UTF-8 含中文的 std::string,它们都会直接失败或误读字节 —— 这时候得先做编码转换,不是换函数就能解决的。










