本文将详细的讲解源于《剑指offer》种一道经典面试题--库函数atoi如何使用,以及它的模拟实现。
1.atoi函数的功能
将字符串转化为整数
2.使用atoi函数的要求
比如:
#include <stdlib.h> int main() { const char* p = " 666";//常量字符串是不可以修改的 //所以用const修饰*p,使指针p指向的内容不能修改 int ret = atoi(p);//参数是指向要转化的字符串的指针 printf("%d", ret); return 0; }
结果:
3.模拟实现atoi库函数
3.1 atoi函数的模拟要求:
不为空指针
if (p == NULL) { return 0;//如果为空指针则直接返回0 }
不为空字符串
if (*p =='\0') { return 0;//如果为空字符串则直接返回0 }
遇到非数字字符
1.要求如果遇到空格,则根据需要丢弃可能多的空格字符,直到找到第一个非空字符。
while (isspace(*s))//如果是空格则返回非0,如果不是则返回0 { s++; }
2.然后从该字符开始,取一个可选字符 + 号或者 - 号
if (*p == '+') { flag = 1;//定义一个标签,如果为+号,则flag为1 s++; } else if (*p == '-') { flag = -1;//如果为-号,则flag为-1; s++; }
超出范围
1.这时就是处理数字字符的转换了
int n = 0; while (isdigit(*s))//如果为数字则返回非0,如果不是则返回0 { n = n * 10 + flag*(*p - '0'); //一个数字字符-'0'=数字; 比如'8'-'0'=8; //flag如果是-1,则最后答案也是负数,如果flag为1则最后答案为正数。 s++; }
2.处理完数字字符转换,我们就要考虑,这个数字字符是否会超出整形的范围呢。
整形的最大值为 INT_MAX
最小值为INT_MIN
还有要注意这里的变量 n是有整形 int 定义的,数字字符再怎么超值,也会因为截断放进整形里面的,所以应该用更大的数long long来定义n
long long n = 0; while (isdigit(*s)) { n = n * 10 + flag*(*s - '0'); if (n > INT_MAX || n < INT_MIN)//超出int类型的范围 { return 0; } s++; }
判断是否合法
上面这个判断是否为数字的while循环,会因为什么原因结束呢?
1.正常遇到字符串最后一个’\0’
如果遇到’\0’,则表明后面没有字符了,数字字符也全部转换了,可以直接返回 n
2.不是数字,循环结束
如果前面是数字字符,后面突然又不是数字字符了,则循环会停止下来,atoi函数要求 字符串可以在构成整数的字符之后包含其他字符,这些字符将被忽略,并且对此函数的行为没有影响。
所以最后也可以直接返回 n。
这两种情况都是合法的
不过你看到现在有没有什么迷惑呢?
我们将不合法的都直接返回0了,这样可以吗?
答案是不可以的。
因为我们不知道返回0是因为非法返回0还是就数字字符转换的0,所以我定义一个枚举变量state
它可能是非法的,也可能是合法的。因为大多数情况下是非法的,只有两种情况是合法的所以我们将它先定义为非法。
当发现是非法时,枚举变量不用改,然后直接返回0,那这个0就是在非法情况下返回的0,
当发现是合法时,我们再将枚举变量state改成合法的,那在这情况下返回的就是合法的。
enum State { INVALE,// 0,默认是非法的 VALE//1,默认是合法的 }state; state = INVALE;//因为大多数情况是非法的,所以一开始让state默认为非法的 /* 设置成全局变量,整体都可以访问*/
最后我们再根据state是否合法来打印n
这样做的好处还有可以将所有的非法情况都直接用一个state =INVALE来表示了,因为还有其他情况我没有讨论,比如 " aa6665",数字前面有字母的没有列举但因为默认是非法的,只有合法是state才表示VALE,所以也没有关系。
最终模拟代码:
#include <ctype.h> #include <stdio.h> #define INT_MAX 2147483647 #define INT_MIN (-2147483647 - 1) enum State { INVALE,//默认是非法的 VALE//默认是合法的 }state; state = INVALE;//因为大多数情况是非法的,所以一开始让state默认为非法的 /* 设置成全局变量,整体都可以访问*/ int digit_change(char* s) { int flag = 1;//正数默认是1 if (s == NULL)//1.空指针 { return 0;//非法情况下的返回0 } if (*s =='\0')//2.空白字符串 { return 0;//非法情况下的返回0 } while (isspace(*s))//如果是空格则返回非0,如果不是则返回0 { s++; } if (*s == '+')//3.非法符号+ - { flag = 1; s++; } else if (*s == '-') { flag = -1; s++; } long long n = 0; while (isdigit(*s))//处理数字字符转换 { n = n * 10 + flag*(*s - '0'); if (n > INT_MAX || n < INT_MIN) { return 0; } s++; } /*跳出来有两种情况,一种是找到\0,另一种是发现不是数字字符,但都是合法的*/ state = VALE;//把state改为合法的 return (int)n; } int main() { char* s = " -112a"; int ret=digit_change(s); if (state == VALE) { printf("合法的%d", ret); } else { printf("非法的%d", ret); } return 0; }
结果: