整数在内存中的存储方式就一句话
**在计算机内存中,整数一律采用补码的形式来存储。当读取整数时要采用逆向的转换,也就是将补码转换为原码。**所以在有符号数和无符号数之间相互转换的时候会出现一些问题。下面会举例说明。
简要说介绍一下原码、反码、补码:
原码:将一个整数转换成二进制形式,就是其原码。
反码:对于正数,它的反码就是其原码(原码和反码相同);负数的反码是将原码中除符号位以外的所有位(数值位)取反。
补码:对于正数,它的补码就是其原码(原码、反码、补码都相同);负数的补码是其反码加 1。
举个例子, 6 和 -18 从原码到补码的转换过程,假设6和-18的数据类型是short类型(16bit),以十六进制表示,读者可以转化成二进制自己理一下:
6→原码0x0006→反码0x0006→补码0x0006
-18→原码0x8012→反码0xffed→补码0xffee
整数运算过程
6 - 18 = 6 + (-18) = [0000 0000 0000 0110]补 + [1111 1111 1110 1110]补 = [1111 1111 1111 0100]补 = [1111 1111 1111 0011]反 = [1000 0000 0000 1100]原 = -12 18 - 6 = 18 + (-6) = [0000 0000 0001 0010]补 + [1111 1111 1111 1010]补 = [1 0000 0000 0000 1100]补 = [0000 0000 0000 1100]补 = [0000 0000 0000 1100]反 = [0000 0000 0000 1100]原 = 12 5 - 13 = 5 + (-13) = [0000 0000 0000 0101]补 + [1111 1111 1111 0011]补 = [1111 1111 1111 1000]补 = [1111 1111 1111 0111]反 = [1000 0000 0000 1000]原 = -8 13 - 5 = 13 + (-5) = [0000 0000 0000 1101]补 + [1111 1111 1111 1011]补 = [1 0000 0000 0000 1000]补 = [0000 0000 0000 1000]补 = [0000 0000 0000 1000]反 = [0000 0000 0000 1000]原 = 8
关于有符号数和无符号数之间相互转换的时候会出现一些问题。
#includeint main() { short a = 0101; //八进制 int b = -0x1; //十六进制 long c = 111; //十进制 unsigned short x = 0xffff; //十六进制 unsigned int y = 0x80000000; //十六进制 unsigned long z = 50; //十进制 //以无符号的形式输出有符号数 printf("a=%#ho, b=%#x, c=%lun", a, b, c); //以有符号数的形式输出无符号类型(只能以十进制形式输出) printf("x=%hd, y=%d, z=%ldn", x, y, z); return 0; }
运行结果
根据输出结果,变量b、x、y与原来的变量赋值对不上号。分析一下原因:
变量b保存的数据在内存中存储的形式是:
b = -0x1 = [1000 0000 …… 0000 0001]原 = [1111 1111 …… 1111 1110]反 = [1111 1111 …… 1111 1111]补 = [0xffffffff]补
b原本是有符号数,当转化成无符号数时,此时b就是一个整数了,补码转化成原码就是补码本身,所以输出的是0xffffffff。
变量x,y保存的数据在内存中的形式是:
x = 0xffff = [1111 1111 1111 1111]补 y = 0x80000000 = [1000 0000 …… 0000 0000]补
当无符号数转换为有符号数时,如果最高位被置1(也就是符号位)了,转换出来的数必定是个负数,原来无符号的时候,原码就是补码,转变为有符号后,补码还是原来的补码,但是,这时的补码是一个负数转换后的补码,就要根据这个补码去找对应的负数。即有
[1111 1111 1111 1111]补 = [1111 1111 1111 1110]反 = [1000 0000 0000 0001]原 = -1 [1000 0000 …… 0000 0000]补 = -231 = -2147483648
分析符合输出结果。



