目录
前言
前面我们说过创建变量时,会在内存中开辟一块空间存放变量,并且变量大小由数据类型决定。字符型占一个字节,短整型占两个字节,整形占四个字节等等,那么当这些变量放进数据后具体又是以何种方式存储的呢?这就不得不先说一下原码,反码和补码了。
一.机器数与真值
在学习原码,反码以及补码之前,必须先了解一下机器数与真值的概念。
1.机器数
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 0表示正数,1表示负数.
例如+2,计算机字长为8位,+2的8位二进制数就是0000 0010(左边高位,右边为低位),如果是-2,其二进制数就是1000 0010。
上面提到的0000 0010以及1000 0010就是机器数。
2.真值
真值即真正的数值。在上面的机器数中,因为最高位是符号位,所以其形式值并不是其真正的数值,形式值就是不把最高位看做符号位的值。而其真正的数值即真值是除去符号位之后剩余二进制的数值。例如-2,其二进制为1000 0010,它的真值是-2,而不是形式值130(1000 0010转化为十进制就是130)。为了区分两者,因此把带符号位的二进制所对应的真正数值称为机器数的真值。
二.原码,反码,补码的概念
由于原码,反码,补码只用于整形数据的存储,因此下面举例皆用整形数据。整形在内存中占4个字节,即32位比特位。需要注意原码,反码,补码只是对同一数据的不同编码方式,表示的都是同一个数值。
计算机中的有符号数有三种表示方法,即原码,反码,补码。
三种表示方法均有符号位和数值位两部分,符号位都是用数字二进制中最高位0表示正,用数字二进制中最高位1表示负,而数值位有三种表示方法,各不相同。
1.原码
原码是指直接按照正负将数字翻译为二进制,最高位表示符号位的二进制码。
例如:
+1的原码:00000000000000000000000000000001(最高位0便是正)
-1的原码:10000000000000000000000000000001(最高位1表示负)
原码是最直观,最容易让人接受和理解的一种码。
2.反码
正数的反码就是正数的原码,即正数的原码与补码相同。
负数的反码就是原码的符号位不变,其余位按位取反,最后得到的就是该负数的反码。
例如:
+1的反码:00000000000000000000000000000001(最高位0便是正)
-1的反码:11111111111111111111111111111110(最高位1表示负)
正数的反码是比较容易理解的,但是负数的反码在原码的基础上取了反,数值形式上发生了改变,所以会让人不容易直观的感受。
3.补码
正数的补码就是原码,即正数的补码与原码相同。
负数的补码就是在负数的反码的基础上再加1,加1后得到的二进制数就是补码。
例如:
+1的补码:00000000000000000000000000000001(最高位0便是正)
11111111111111111111111111111110(-1反码)
+00000000000000000000000000000001(1的二进制数)
=11111111111111111111111111111111-1的补码:11111111111111111111111111111111(最高位1表示负)
- 正数的原码,反码,补码相同。
- 负数的反码等于符号位不变,其余位按位取反。
- 负数的补码等于反码加1。
三.原码反码补码存在的原因
通过上面的讲述,现在我们知道了一个数可由三种编码方法表示,即原码,反码和补码。先来看一组对比。
+1的原码:00000000000000000000000000000001
+1的反码:00000000000000000000000000000001
+1的补码:00000000000000000000000000000001
-1的原码:10000000000000000000000000000001
-1的反码: 11111111111111111111111111111110
-1的补码:11111111111111111111111111111111
我们可以发现正数的原码反码补码都一样,很直观,但是负数的原码反码补码之间的差别就很大,很不容易看出其表示的具体数值,那么,既然已经有可以直观的表示数值的原码,为什么还会存在反码与补码呢?
上面我们说过,用数值二进制的最高位表示符号位,即0表示正,1表示负。但是你有没有想过一个问题,我们人脑可以很清楚的区分这样的表示,在计算时可以根据符号位对对应真值进行加减。但是计算机无法像人的大脑一样进行区分。我们知道在计算机的CPU中只有加法器,两个正数相加或许没什么问题,但是如果是减法呢?例如1-1,我们知道减法的运算法则可以用加来代替,即1-1可以写做1+(-1),此刻假若计算机用原码计算,就是
00000000000000000000000000000001
+ 10000000000000000000000000000001
=10000000000000000000000000000010,即-2
显然结果是不正确的,此时,为了解决这一问题就出现了反码,现在我们用反码再计算一下试试:
00000000000000000000000000000001
+ 11111111111111111111111111111110
=11111111111111111111111111111111
反码相加得到的依然是反码,所以需要对其进行还原为原码10000000000000000000000000000000,即-0。
可能有的小伙伴会觉得博主上面写的-0中的负号毫无意义,因为在人们的理解中无论是+0还是-0,不就是0吗。但是以这种想法思考,那么意思就是-0表示0,+0也表示0,即
00000000000000000000000000000000表示0
10000000000000000000000000000000也表示0
显然这是不对的,如果是这样,就产生歧义了。这个时候补码的出现就解决了这一问题。
我们知道正数的补码就是原码本身,负数的原码等于负数的反码加1.现在我们用补码再计算一遍
1-1,即1+(-1)。
00000000000000000000000000000001
+ 11111111111111111111111111111111
=100000000000000000000000000000000
上面这个结果是33位,这是博主为了小伙伴们方便理解故意这么写的。实际上它的结果应该是
00000000000000000000000000000000,我们知道一个整形是4个字节即32位比特位,所以进位的1溢出了,存不下而舍去。最终的结果就是00000000000000000000000000000000,即0。结果计算正确。所以为了避免歧义与方便计算,计算机中存储整数时皆是以补码的形式进行存储。我们知道补码是由原码符号位不变,其余位按位取反,再加1得来,其实补码符号位不变其余位按位取反,再加1得到的就是原码。我们在计算机屏幕上显示时,可定需要将补码再返回到原码,然后展示其对应的数值。用补码存储的另一个好处就是计算机用计算补码的那套硬件电路就能够用其再将补码翻译回原码而不需要增加多余的硬件电路,极大地简化了计算机硬件设计。
四.例题
学习了原码反码补码,当然要知道其在题目中的应用,下面我们用一道例题来结束今天的学习吧。
例题:
#include<stdio.h>
int main()
{
unsigned int ch = -10;
printf("%u\n", ch);
printf("%d\n", ch);
return 0;
}
小伙伴们可以先思考一下打印结果。
#include<stdio.h>
int main()
{
unsigned int ch = -10;
//原码:10000000000000000000000000001010
//反码:11111111111111111111111111110101
//补码:11111111111111111111111111110110
//用16进制表示就是:0xFF FF FF F6
//
//printf("%u\n", ch);//%u是打印无符号数,意思是你要我打印的一定是无符号数,不是无符号数,我也认为是无符号数
//打印结果为4294967286
printf("%d\n", ch);//%d是打印有符号数,意思是你要我打印的一定是有符号数,不是有符号数,我也认为是有符号数
//打印结果为-10
return 0;
}
刹国(结束)!!!