浅谈将一个字符串转换为整数

前一段时间刷到“把一个字符串转换为整数”这道题。据说是微软面试开发工程师最常用到的一个问题。写出了最终版本之后,竟然花费了大量的时间,这让我感到自己考虑问题还不是那么全面。所以,以此为例,一来想好好记住这个解题方法;二来是想要保持逻辑上这种解题思路。做以总结。

很多人拿到题都会迅速的写出如下代码:


 int transform(char* string){

   int num=0;
   while(*string++){
   num=num*10+num-'0';
 
 }
   
   return number;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

       乍一看之下,可能会觉得逻辑实现没有什么问题,但是如果面试的时候你写出了这样的代码,那绝对是没有生还的希望。因为这样的代码是在是态Low了(灬ꈍ ꈍ灬)

       通常题目越是简单,面试官的期望就会越高。这道题,除了完成基本功能之外,我们还需要考虑到:
1.边界条件
2.错误处理
3.最大的正整数溢出和最小的负整数溢出
4.正负号
5.输入中有非数字字符

看看我们列出的这5点,你再想想自己是不是之前考虑的方面太少,代码是不是很挫?
ok,先拿出最终改良版的代码:

#include<iostream>
#include<windows.h>
using namespace std;


//1.把一个字符串转换为整数


	int string2int(char* string){
	
		if (*string==''||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;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

其实最终版本的代码也并不复杂,也都可以分析明白。但是在这里,我还是有一个点要说一下。那就是在判断溢出的时候:

if ((flag == 1 && num>0x7fffffff) 
				    || (flag == -1 && num < (int)0x80000000))
  • 1
  • 2

在分析这块的时候,我有花费了一些时间研究负数溢出的判断条件为什么是这种写法。我们知道在计算机中的数字都是采用补码表示的。比如我们要想打印-1,

        int a =0xffffffff;
		cout << a << endl;
//打印结果: -1
  • 1
  • 2
  • 3

       我们知道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 表示就行了。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

       这样一来,有符号的 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;

原文:https://blog.csdn.net/tonglin12138/article/details/91352612

原文地址:https://www.cnblogs.com/Ph-one/p/13502178.html