文章主要介绍atoi的模拟实现,包含具体实现思路以及代码讲解;同时,对MSDN中atoi的作用进行了仔细介绍。 注:第一部分是在MSDN中的注解,第二部分包含笔者对函数的分析以及具体实现,第三部分则是笔者对于此模拟实现的一些缺陷说明。
一、atoi在MSDN中的注解
atoi函数将字符串转换为整型;
1、返回值
当字符串可以转换为整型时,函数会返回一个整型的值(对于atoi); 不可以转换时,会返回0; 若产生溢出,返回值未定义,也就是说:数据溢出的话,atoi的处理会出错;
2、参数
需要转换的字符串;
3、标注
函数会将字符串转换为特定类型的数据,然后返回;在第一个字符未识别到数字时,函数会停止识别;用来识别的字符串可以是用于终止字符串的空字符\0;
二、具体实现
1、原函数测试
分析:
通过在MSDN中函数的描述,我们对atoi已经有了大概想法:
- 函数将字符串转换为整型数据,然后返回;
- 在识别第一个字符时,若遇到非数字,则会停止转换数据;
- 转换成功时,返回转换值,失败则返回0;
- 对于数据溢出的情况,atoi没有明确规定;
思考:
做了上述分析,但有些情况我们并不清楚,比如说:
- 函数遇到空指针;
- 函数第一个字符为+号或-号;
- 函数遇到空白;
- 字符串中同时存在数字与非数字的识别情况;
测试
-
首先,遇到空指针时: 函数报错,故我们应限制,不可输入NULL;
-
遇到+号或-号时: -号会返回负值; +号返回正值; 也就是说,atoi函数的识别会包括 + - 符号;
-
遇到空白时: 函数会跳过空白字符,而在空白字符后的第一个字符开始转换;
-
字符串同时存在数字与非数字: 非数字位于数字中: 函数在遇到非数字时停止转换; 数字位于非数字中:函数未进行转换,便返回0; 数字中包含\0时: 由此可知:‘\0’也仅作为非数字字符串的一种,与非数字的处理相同;
2、实现
根据上述分析,已经清楚: atoi函数具有以下特点
- 遇到非数字字符,停止识别,遇到数字字符,逐个识别为整型值; 定义内置compute函数,每识别一个字符,加到个位,同时原数据×10;
int compute(const char* s)
{
int ret = 0;
while (isdigit(*s))
{
ret = ret * 10 + (*s++ - '0');
}
return ret;
}
- 遇到空白会跳过; 由于空白的字符表示是‘\t’,因此只需循环跳过字符串开始的空白即可;
while ((*s++) == '\t');
- 只在除去空白后的第一个位置识别正负符号; 正负号的判断在第一个位置执行,因此只需一次if语句,判断即可;
if (isdigit(*s))
{
ret = compute(s);
}
else if (*s == '-')
{
ret = -compute(s + 1);
}
else if (*s == '+')
{
ret = compute(s + 1);
}
else
{
return 0;
}
- 存在边界问题,数据溢出返回值无法预测; 此处笔者暂时未想到合适的方法判断数据溢出,在上述的实现中,若存在数据溢出,则返回值不可靠;
三、模拟实现
总代码:
由具体分析实现代码如下:
//计算转换字符值
int compute(const char* s)
{
int ret = 0;
while (isdigit(*s))
{
ret = ret * 10 + (*s++ - '0');
}
return ret;
}
int my_atoi(const char* s)
{
//判空
assert(s);
int ret = 0;
//跳过空白
while ((*s++) == '\t');
//判别第一个字符
if (isdigit(*s) || *s == '+')
{
ret = compute(s);
}
else if (*s == '-')
{
ret = -compute(s + 1);
}
else
{
return 0;
}
return ret;
}
缺陷:
- 无法判断数据是否溢出; 当数据超过int可表示的最大值时(也就是除符号位外全为1): 则会将后续的1顶到符号位上,从而造成数据的不准确。