整数在内存中的存储方式就一句话
**在计算机内存中,整数一律采用补码的形式来存储。当读取整数时要采用逆向的转换,也就是将补码转换为原码。**所以在有符号数和无符号数之间相互转换的时候会出现一些问题。下面会举例说明。
简要说介绍一下原码、反码、补码: 原码:将一个整数转换成二进制形式,就是其原码。 反码:对于正数,它的反码就是其原码(原码和反码相同);负数的反码是将原码中除符号位以外的所有位(数值位)取反。 补码:对于正数,它的补码就是其原码(原码、反码、补码都相同);负数的补码是其反码加 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
关于有符号数和无符号数之间相互转换的时候会出现一些问题。
#include <stdio.h>
int 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=%lu\n", a, b, c);
printf("x=%hd, y=%d, z=%ld\n", 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
分析符合输出结果。
|