57. atoi 的实现:如何将字符串转换为整数?
字符串转整数,这个函数大家肯定不陌生。C 标准库里的 atoi,用起来简单,但真要自己手写一个,坑还真不少。
我记得刚入行那会儿,有个同事直接用了 atoi 处理用户输入,结果输入了个超大数,程序直接崩了。嗯,从那以后我就养成了一个习惯——能自己写就自己写,至少心里有底。
atoi 到底做了什么?
说白了,就是把一串字符变成整数。比如 "123" 变成 123,"-42" 变成 -42。
但实际场景比这复杂。你想想看,用户可能输入空格、正负号、甚至非法字符。一个健壮的 atoi,必须把这些情况都处理好。
核心逻辑:三步走
我个人习惯把 atoi 的实现拆成三个步骤:
- 跳过前导空白——空格、制表符、换行符统统跳过
- 处理正负号——遇到
'+'或'-'记录符号 - 逐字符转换——遇到非数字字符就停止
这里有个细节:atoi 遇到非法字符不会报错,直接返回已转换的部分。这其实是个隐患,我在项目中遇到过因为输入里混了个字母,结果数据对不上的情况。
手写一个 atoi
来看代码。我尽量写得清晰一些,该处理的边界都处理了。
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
int my_atoi(const char *str) {
if (str == NULL) return 0;
// 1. 跳过前导空白
while (isspace((unsigned char)*str)) {
str++;
}
// 2. 处理正负号
int sign = 1;
if (*str == '-') {
sign = -1;
str++;
} else if (*str == '+') {
str++;
}
// 3. 逐字符转换
int result = 0;
while (isdigit((unsigned char)*str)) {
int digit = *str - '0';
// 检查溢出
if (result > (INT_MAX - digit) / 10) {
return (sign == 1) ? INT_MAX : INT_MIN;
}
result = result * 10 + digit;
str++;
}
return result * sign;
}
这段代码里,我特别加了一个溢出检查。为什么?因为 INT_MAX 是 2147483647,如果输入 "2147483648",直接乘 10 再加 8 就溢出了。我曾经在嵌入式设备上吃过这个亏,整型溢出后数据直接翻转,排查了好久才发现。
溢出检查的原理
你可能会问:为什么用 result > (INT_MAX - digit) / 10 这个写法?
其实很简单。我们想判断 result * 10 + digit 会不会超过 INT_MAX。直接乘可能会溢出,那就先做除法:
- 如果
result > INT_MAX / 10,那乘 10 肯定超了 - 如果
result == INT_MAX / 10,就看digit会不会让最后一位超限
这个技巧在嵌入式开发里很常用。毕竟很多芯片没有硬件除法,但整数比较还是很快的。
边界情况一览
| 输入 | 预期输出 | 说明 |
|---|---|---|
"42" |
42 | 正常情况 |
" -42" |
-42 | 前导空格 + 负号 |
"4193 with words" |
4193 | 遇到非数字停止 |
"words and 987" |
0 | 开头就不是数字 |
"-91283472332" |
INT_MIN | 溢出,返回最小值 |
"" |
0 | 空字符串 |
NULL |
0 | 空指针 |
注意:标准库的
atoi 在溢出时行为是未定义的。也就是说,不同编译器可能给出不同结果。所以我在手写版本里明确返回了 INT_MAX 或 INT_MIN,这样行为可预测。
流程图:atoi 的执行路径
下面这张图把整个流程串起来了。你可以对照着代码看,会更清楚。
小技巧:如果你在嵌入式环境里用
atoi,建议用 strtol 替代。它提供了更完善的错误检测,能区分「转换结果为 0」和「转换失败」。我在做物联网设备时,就统一用 strtol 处理配置字符串,省了不少调试时间。
常见错误与避坑
- 忘记处理前导空格——用户输入经常带空格,不跳过就直接报错
- 符号位处理不当——比如
"+-42"这种输入,标准做法是返回 0 - 溢出检查缺失——这是最危险的,可能导致安全漏洞
- 忽略空指针——传入 NULL 直接崩溃,这在嵌入式里是致命错误
核心要点:
- 先跳过空白,再处理符号,最后逐位转换
- 溢出检查用除法,别直接乘
- 遇到非法字符立即停止,返回已转换部分
- 空指针和空字符串返回 0
写 atoi 看起来简单,但真正做好不容易。我每次面试新人,都会让他们手写这个函数。能写出完整溢出检查的,基本代码功底都不差。你试试看,能不能一次写对?