前一段时间刷到“把一个字符串转换为整数”这道题。据说是微软面试开发工程师最常用到的一个问题。写出了最终版本之后,竟然花费了大量的时间,这让我感到自己考虑问题还不是那么全面。所以,以此为例,一来想好好记住这个解题方法;二来是想要保持逻辑上这种解题思路。做以总结。
很多人拿到题都会迅速的写出如下代码:
int transform(char* string){
int num=0;
while(*string++){
num=num*10+num-'0';
}
return number;
}
乍一看之下,可能会觉得逻辑实现没有什么问题,但是如果面试的时候你写出了这样的代码,那绝对是没有生还的希望。因为这样的代码是在是态Low了(灬ꈍ ꈍ灬)
通常题目越是简单,面试官的期望就会越高。这道题,除了完成基本功能之外,我们还需要考虑到:
1.边界条件
2.错误处理
3.最大的正整数溢出和最小的负整数溢出
4.正负号
5.输入中有非数字字符
看看我们列出的这5点,你再想想自己是不是之前考虑的方面太少,代码是不是很挫?
ok,先拿出最终改良版的代码:
#include<iostream>
#include<windows.h>
using namespace std;
//1.把一个字符串转换为整数
int string2int(char* string){
if (*string=='\0'||string == NULL)
return -1;
int flag = 1;
int num = 0;
char *_str = string;
//考虑到正负号
if (*_str == '+') //解引用 高于 ==的优先级:
{
flag == 1;
++_str;
}
else if (*_str == '-')
{
++_str;
flag = -1;
}
while (*_str){
if (*_str > '0' && *_str < '9'){
num = num * 10 + flag*(*_str - '0');
//判断数据是否溢出
if ((flag == 1 && num>0x7fffffff)
|| (flag == -1 && num < (int)0x80000000))
return -2; //溢出
}
else{
//输入的字符串中不全是数字
return -1; //不合法
}
++_str;
}
return num;
}
void test1(){
char* str = "-1234";
int n = string2int(str);
cout << n << endl;
}
int main(){
test1();
system("pause");
return 0;
}
其实最终版本的代码也并不复杂,也都可以分析明白。但是在这里,我还是有一个点要说一下。那就是在判断溢出的时候:
if ((flag == 1 && num>0x7fffffff)
|| (flag == -1 && num < (int)0x80000000))
在分析这块的时候,我有花费了一些时间研究负数溢出的判断条件为什么是这种写法。我们知道在计算机中的数字都是采用补码表示的。比如我们要想打印-1,
int a =0xffffffff;
cout << a << endl;
//打印结果: -1
我们知道char类型的表示范围是-128~127。正数表示范围是0-127一共2的7次方128个数;那为什么负数可以表示-128呢?我们应该注意到-0的存在。
-0 :原码 1000 0000 的补码为 1 0000 0000
由于 char 是 八位 ,所以取低八位 00000000。
+0 :原码 0000 0000 ,补码为也为 0000 0000
虽然补码 0 都是相同的,但是有两个 0 ,既然有两个 0 ,况且 0 既不是正数,也不是
负数, 用原码为 0000 0000 表示就行了。
这样一来,有符号的 char,原码都用来表示 -127~127 之间的数了,唯独剩下原码 1000 0000 没有用,用排列组合也可以算出来,能表示 2^7 = 128 个数,刚好是 0~127。也能表示 128 个数,总共 signed char 有 256 个数,这与 -127~127 中间是两个 0 刚好吻合。
现在再来探讨一下关于剩下的那个 1000 0000,既然 -127 ~ 0 ~ 127 都有相应的原码与其对应,那么 1000 0000 表示什么呢,当然是 -128 了,为什么是 -128 呢,网上有人说 -0 即 1000 0000 与 128 的补码相同,所以用 1000 0000 表示 -128,,这我实在是不敢苟同,或者说 -128 没有原码,只有补码 1000 0000,胡扯,既然没有原码何来补码,还有说 -128 的原码与 -0(1000 0000) 的原码相同,所以可以用 1000 0000 表示 -128,我只能说,回答得不要那么牵强, 原码 1000 0000 与 -128 的原码实际上是不同的。
但为什么能用它表示 -128 进行运算,如果不要限制为 char 型(即不要限定是 8 位),再来看,-128 的原码:1 1000 0000 ,9位,最高位符号位,再算它的反码:1 0111 1111,进而,补码为:1 1000 0000,这是 -128 的补码,发现和原码一样,1 1000 0000 和 1000 0000 相同?如果说一样的人真是瞎了眼了,所以,-128 的原码和 -0(1000 000) 的原码是不同的,但是在 char 型中,是可以用 1000 000 表示 -128 的,关键在于char 是 8 位,它把 -128 的最高位符号位 1 丢弃了,截断后 -128 的原码为 1000 000 和 -0 的原码相同,
也就是说 1000 0000 和 -128 丢弃最高位后余下的 8 位相同,所以才可以用 -0 表示 -128,这样,当初剩余的 -0(1000 0000),被拿来表示截断后的 -128,因为即使截断后的 -128 和 char 型范围的其他 (-127~127) 运算也不会影响结果, 所以才敢这么表示 -128。
小笔记:
对于char类型,最大的正数表示:0x7f , 最大的 负数表示 :0x80;
对于int类型,最大的正数表示:0x7fffffff,最大的负数表示: 0x80000000;