文章目录
原码、真值、反码、补码
在计算机的世界中,一切的数据都是通过二进制的0和1进行存储和运算等操作的,因此产生了原码、真值、反码以及补码的概念。
-
原码
计算机中使用的是二进制的0和1,而人类日常生活中使用的是有正负之分的十进制数,所以,在使用二进制表示十进制数字的时候,规定最高位数为符号位(0表示正数,1表示负数),其余位数表示数字的绝对值,两者合在一起称作原码。
-
真值
在原码的基础上除去符号位,剩余位数表示的十进制数就是真值。
-
反码
原码出现之后可以对数字进行运算,但是如果应用到负数的加减运算之后会出现错误,例如:
1 + (-2) = 0000 0001(原) + 1000 0010(原) = 1000 0011(原) = -3于是引入了反码的概念,规定正数的反码是它的原码本身,负数的反码为原码保留其符号位,其余位取反,例如:
1+ (-2)= 0000 0001(反) + 1111 1101(反) = 1111 1110(反) = 1000 0001(原) = -1
-
补码
反码的出现解决了涉及负数运算的问题,但是同时也引申出新的问题,在人类的观念里0是没有正负之分的,但是如果使用反码计算就会出现 +0 和 -0 的问题,到底使用 0000 0000 还是 1000 0000 来表示0这个数字呢?
1 + (-1) = 0000 0001(反) + 1111 1110(反) = 1111 1111反) = 1000 0000(反) = -0?所以又引入了补码的概念,规定正数的补码是它的原码本身,负数的补码是其反码加1。这样就可以用 0000 0000 来表示原本的0:
1 + (-1) = 0000 0001(补) + 1111 1111(补) = 0000 0000(补) = 0(10)而原本的 1000 0000 可以用来表示 -128 的补码(但是对于八位二进制内,-128 没有原码和反码表示)
(-1)+ (-127) = 1111 1111(补) + 1000 0001(补) = 1000 0000(补) = -128(10)
系统的大端和小端
计算机系统以字节为单位,内存中每个地址单元都对应着一个字节。对于使用多个字节的大型数据,就需要使用大端或小端对每个字节进行储存。
- 大端:高位字节排在内存的低地址端,低位字节排在内存的高地址端
- 小端:高位字节排在内存的高地址端,低位字节排在内存的低地址端
以十六进制数0x12345678分别在大小端系统中储存的方式为例:
- 大端:0x78 0x56 0x34 0x12
- 小端:0x12 0x34 0x56 0x78
#include <stdio.h>
int main(void) {
unsigned int a = 0x12345678;
char *b = (char *)&a; // 取第一个字节的地址
// 0x12 -> 大端
// 0x78 -> 小端
if (*b == 0x12) {
printf("大端\n");
} else {
printf("小端\n");
}
return 0;
}
采用大端方式进行数据存放符合人类的正常思维,比较容易判断正负,符号位的判定固定为第一个字节。而采用小端方式进行数据存放利于计算机处理,强制转换类型时只需要在右边补全相应位数的0即可。
不同的CPU就大小端并没有达成一致。常用的PC使用的多为小端储存,但是TCP/IP协议中数据是按照大端格式存放的,所以使用小端储存的机器必须对发送和接收的数据进行转换才能实现网络通信。