先分析一波:
atoi
的函数原型为:int atoi( const char *str );
atoi
函数的作用是:扫描参数str
字符串,跳过前面的空白字符(例如空格,tab缩进等,可以通过isspace()
函数来检测),直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('\0'
)才结束转换,并将结果返回。- 返回转换后的整型数;如果
str
不能转换成int
或者str
为空字符串,那么将返回0
。 - 如果需要模拟实现
atoi
,下面是我们需要注意的几个问题:
- 如果传入的串为空串,那么返回
0
,并且标记为非法。 - 当
str
字符串传入时,开始扫描,如果前面为空白字符,那么将这些位置跳过。 - 当空白字符全部跳过时,如果遇到正负号,那么转换开始,如果未遇到,默认为正数。
- 如果转换的过程中,数字溢出,那么返回
0
,并且标记返回值非法。 - 如果在转换的过程中遇到非数字字符,那么函数返回,并且返回值为当前以转化的值,标记返回值合法。如果遇到的第一个字符即为非数字字符,函数返回值为
0
,并标记为合法。 - 标记是通过定义的枚举类型的全局变量实现的,枚举的成员为:
VALID
和INVALID
,标记的默认值为INVALID
,因为在判断中大多数情况都是无效的值。 - 如果转换的值不合法,那么不对转化结果进行输出。
- 如果传入的串为空串,那么返回
下面我们对上述问题逐一实现:
str
的空串问题:
if (*str == '\0') //第一个字符即为\0
return 0;
str
的空白字符问题:
while (isspace(*str)) //头文件为ctype.h
{
str++;
}
- 正负号问题
if (*str == '-')
{
flag = -1; //flag的值用来与新获取的数字相乘再与记录的变量相加。
str++;
}
else if (*str == '+')
{
str++;
}
- 遇到非数字字符问题以及溢出问题
if (isdigit(*str))
{
ret = ret * 10 + (*str - '0') * (flag);
//溢出问题
if (ret > INT_MAX || ret < INT_MIN)
{
return 0;
}
}
else
{
//字符中的非数字字符
state = VALID;
return (int)ret;
}
上面即为出现各种情况的处理方法,下面我们只需要将这些方法结合到一起,就可以简易的实现atoi。
#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include<limits.h>
enum State //标记返回的数值是否合法
{
VALID,
INVALID
};
static enum State state = INVALID; //默认值为不合法
int my_atoi(const char *str)
{
long long ret = 0; //要判断是否溢出,所以让值存储在比int大的类型中,与int能存储的最大值进行比较
int flag = 1; //区别正负值问题
assert(str != NULL); //空指针
if (*str == '\0') //空串
return 0;
//前面为空字符
while (isspace(*str)) //头文件为ctype.h
{
str++;
}
//正负号问题
if (*str == '-')
{
flag = -1;
str++;
}
else if (*str == '+')
{
str++;
}
while (*str)
{
//是否为数字字符问题
if (isdigit(*str))
{
ret = ret * 10 + (*str - '0') * (flag);
//溢出问题
if (ret > INT_MAX || ret < INT_MIN)
{
return 0;
}
}
else
{
//字符中的非数字字符
state = VALID;
return (int)ret;
}
str++;
}
state = VALID;
return (int)ret;
}
int main()
{
int ret = 0;
char p[20] = {0};
printf("Please input->_: ");
scanf("%s", p);
ret = my_atoi(p);
if(state == VALID) //对模拟实现的函数来说 如果转化值不合法,则不进行输出
printf("my_atoi(p) = %d\n", ret);
printf("atoi(p) = %d\n", atoi(p));
return 0;
}
函数运行效果: