一、前提知识:
注:部分来源于互联网(该部分非原创)
计算机内存数值存储方式是补码
原码
一个数的原码(原始的二进制码)以最高位做符号位,0表示该数为正数,为1表示为负数。剩下的位就是该数值绝对值的二进制数。所以负数的原码是在其绝对值的基础上,再加一个最高位1。原码方便人理解,但是当两个正数相减或不同符号数相加时(即十进制加减法计算后如果可能产生负数结果时),必须先比较两个数绝对值大小,才能决定谁减谁并且确定结果是正还是负,所以原码不便于直接加减运算。
反码
正数的反码与原码相同;负数的反码是其原码的符号位不变,剩下部分取反(1、0互变)。反码计算也同样不方便(因为还是存在符号位)
补码
正数的原码、反码、补码全部相同;负数的补码是其反码加1,所以负数的补码是其原码符号位不变,剩下部分取反,最后整个数加1得到补码。
补码符号位不动,其他位求反,最后整个数加1,得到原码。
在计算机系统中,数值均用补码来存储,主要原因是:
- 可以统一0的编码
- 可以把符号位和其它位统一处理
- 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃
不管以原码方式存储,还是以反码方式存储,0都两种表示形式(即-0与+0)。
但是如果以补码方式存储;+0是原码、反码、补码相同,都是全0,;-0原码是10000000(以8位二进制数为例),将其1舍去。
总结
有符号数=1位符号位+n-1位数据位
无符号数=n位数据位
二、int、unsigned int型负数
12345的二进制表达为11 0000 0011 1001
若定义一个int型变量a,则a在内存空间中占32bits。
-12345以补码的方式存在计算机里面,即1111 1111 1111 1111 1100 1111 1100 0111。
a=-12345u即把a的空间里填上补码1111 1111 1111 1111 1100 1111 1100 0111。
读取若以unsigned的方式去读取(%u)即不考虑符号位,直接把补码1111 1111 1111 1111 1100 1111 1100 0111这个二进制数转换成十进制,也就是4294954951。
但是以signed方式读取(%d)就会考虑符号位,还是-12345。
所以把a定义为unsigned型数据也和定义为int的结果一样
另外-12345不带u和带u的结果一样。
三、short型负数
53191是-12345(默认int型)在计算机里的补码1111 1111 1111 1111 1100 1111 1100 0111截取最小的16位直接转换成十进制得到53191。
我之前认为
short a;
a=-12345u;
printf(“%u”,a);
应该打印出来53191。
但是
其实是因为%d输出会把short的16位自动补齐为32位
补齐规则是看最高位:如果最高位为0,就把最高的16位都补为0;如果....1.....补为1.....,然后再输出。
所以是把截取的1100 1111 1100 0111补齐为1111 1111 1111 1111 1100 1111 1100 0111,也
就是4294954951与-12345。
如果用-123456:
因为123456的二进制形式是1 1110 0010 0100 0000(共17位大于short的16位),
所以常数-123456(是int型),补码即为1111 1111 1111 1110 0001 1101 1100 0000
而a是short所以只截取了后16位0001 1101 1100 0000,所以不论是%d还是%u都是补齐为0000 0000 0000 0000 0001 1101 1100 0000,即7616。
而且a的size不会因为-123456后加了u还是l而改变。