题目来自剑指Offer
题目:把字符串转换成整型数,与库函数atoi的功能类似。
原型: int atoi(const char *nptr);
举例:
"123" : 123 "-123" : -123 "00123/2" : 123 "-123/2"; : -123 "- 123/2" :0 "10522545459" : 2147483647 越界 "2147483647"; : 2147483647 刚好不越界 "-21474836480"; : -2147483648;越界 "-2147483648"; : -2147483648 刚好不越界 除此之外,系统提供的函数还可以自动除去起始端的空格,Tab等字符,这里不再实现该功能 " 123" : 123 " -123" : -123总结下要实现的功能:
(1)遇到非法字符,直接输出目前已经转化成功的结果。
(2)越界后,直接输出最大值或最小值。
要注意的几个要点:
(1)判断数字的正负:检测字符串第一个字符是否为+或-
(2)判断字符的合法性,不合法直接返回。
(3)判断是否越界。
对于前两个要点,没什么技巧可言。在处理是否越界上, 这里给出两种方法。
方法一:使用一个类型更大的变量保存中间结果
可以中间结果保存到一个long long类型的变量llNum中,由于int最大值为2147483647,当llNum大于该最大值时,可以认为已经越界。对于最小值-2147483648,可以根据llNum是否小于该最小值,来判断是否越界。
注意:保存中间结果的变量不能是long型的,因为long和int一样都是4个字节,其能表示的数字区间是一样的,而long long是八个字节,表示数字的区间大。这里其实投了一个巧,后面给出了一个判断是否越界的方法。
代码:
#include<iostream> #include <limits> #include <assert.h> using namespace std; int Atoi(const char *pStr) { assert(pStr); long long llNum = 0;//不能写成long,因为long和int都是四个字节 const char* pCur = pStr; bool bIsPositive = true; int MAXINT = std::numeric_limits<int>::max(); int MININT = std::numeric_limits<int>::min(); //判断第一个字符 if (*pCur != 0 && *pCur == '-') { bIsPositive = false; pCur++; } //判断其他字符 while (*pCur != 0) { if (*pCur >= '0' && *pCur <= '9') { llNum = llNum * 10 + *pCur - '0'; if (bIsPositive && llNum >= MAXINT) { return MAXINT; } if (!bIsPositive && -llNum <= MININT) { return MININT; } } else { break; } pCur++; } if (bIsPositive) { return static_cast<int>(llNum); } else { return -static_cast<int>(llNum); } } int main() { //char str[10] = "-12345678"; //char str[100] = "123456789999999996666666"; //char str[100] = "-123456789999999996666666"; //char str[100] = "1234567s899"; //char str[100] = ""; //char str[100] = "-1/"; //char str[100] = " 123/2"; //char str[100] = "00123/2"; //char str[100] = " -123/2"; //char str[100] = "- 123/2"; //char str[100] = "10522545459"; //char str[100] = "2147483647"; //char str[100] = "21474836470"; //char str[100] = "-2147483648"; char str[100] = "-21474836480"; cout<<Atoi(str)<<endl; cout<<atoi(str)<<endl; system("pause"); return 1; }问题:
在判断数字是否越界时,上述代码使用一个能够表示数字区间更大的变量来进行保存中间结果,之后与最大值或最小值进行比较来判断是否越界。但是实质上,还是没给出一个能够判断是否越界的方法。
方法二:本思想参考v_JULY_v博客:程序员编程艺术第三十~三十一章:字符串转换成整数,通配符字符串匹配
本方法和上述方法的区别:中间结果使用int来保存 + 判断越界代码不同
思想:在中间结果执行乘10操作之前,先与最大数除10相比,判断是否溢出。
由于使用int来判断某一个中间结果是否越界,此时就不能在执行语句Num = Num * 10 + *pCur - '0';后在和最值比较是否溢出。这是因为,如果某次的中间结果Num刚好不越界,但是执行该句话后,刚好越界,由于num是int,越界后num保存的值就也就不是Num * 10 + *pCur - '0';的值了。此时拿已经溢出后的错误结果和最值比较肯定就出现错误了。
这里给出大概的方法:
既然我们不能先执行中间结果乘以10的操作(语句Num = Num * 10 + *pCur - '0';),后与最值比较,那么我们可以在执行乘10操作之前,先比较中间结果num与 最值/10的大小。如果num > 最值/10且 nNum == MAXINT/10 && nBit >= MAXINT%10 则此时已经越界,可以不再执行下去。
改进的代码:
#include<iostream> #include <limits> //std::numeric_limits #include <assert.h> using namespace std; int Atoi(const char *pStr) { assert(pStr); int nNum = 0;//与方法一代码区别处 int nBit = 0; const char* pCur = pStr; bool bIsPositive = true; int MAXINT = std::numeric_limits<int>::max(); int MININT = std::numeric_limits<int>::min(); //判断第一个字符 if (*pCur != 0 && *pCur == '-') { bIsPositive = false; pCur++; } //判断其他字符 while (*pCur != 0) { if (*pCur >= '0' && *pCur <= '9') { nBit = *pCur - '0'; //与方法一代码区别处 if (bIsPositive && (nNum > MAXINT/10 || (nNum == MAXINT/10 && nBit >= MAXINT%10))) { return MAXINT; } if (!bIsPositive && (nNum > -(MININT/10) || (nNum == -(MININT/10) && nBit >= -MININT%10))) { return MININT; } nNum = nNum * 10 + nBit; } else { break; } pCur++; } if (bIsPositive) { return nNum; } else { return -nNum; } } int main() { //char str[10] = "-12345678"; //char str[100] = "123456789999999996666666"; //char str[100] = "-123456789999999996666666"; //char str[100] = "1234567s899"; //char str[100] = ""; //char str[100] = "-1/"; //char str[100] = " 123/2"; //char str[100] = "00123/2"; //char str[100] = " -123/2"; //char str[100] = "- 123/2"; //char str[100] = "10522545459"; //char str[100] = "2147483647"; //char str[100] = "21474836471"; char str[100] = "21474836470"; //char str[100] = "-2147483648"; //char str[100] = "-21474836480"; cout<<Atoi(str)<<endl; cout<<atoi(str)<<endl; system("pause"); return 1; }