1.1 计算机编码

1.1.1 比特、字节与字符

比特(bit)是计算机世界里最小的单元,每一个bit的取值是0或1,这便是二进制。1个字节由8个bit构成。字节是最小的存储单位。

字符是个很宽泛的概念,人们生产生活中用到的约定俗成的符号都可以称为字符,主要包含语言字符、数字字符、运算符、特殊字符等。其中,因各国语言不同,语言字符也各不相同。例如,英文包含26个英文字符,中文汉字字符近10万个,数字字符为0到9的数字,运算符包含加、减、乘、除、与、或、非、异或等符号。字符在计算机系统里是以1个或多个字节来表示的,这个由编码来决定,后面小节会详细阐述。

1.1.2 字节对齐

计算机的存储空间按照字节来划分,理论上任何类型的数据都可以从任意位置开始。但实际情况是不同类型的数据需要按照一定的规则排布在指定的内存地址(一般由编译器来指定)。排布规则里很重要的一条便是字节对齐。

如果不按照字节对齐,则会出现内存访问性能降低的问题。一般来说,各种架构的硬件平台都是从偶地址开始读取数据的,如果给定一个奇地址的数据,则需要将其拆分成两次读取,一次从小于奇地址的某个偶地址读取一部分数据,一次从大于奇地址的某个偶地址读取另一部分数据,再将两部分数据拼装成目标数据。

如果一个对象或结构体包含多个字段,那么字节对齐可以出现在字段之间,也可以出现在对象或结构体的尾部,具体规则完全依赖编译器的实现。

1.1.3 数字的表示

数字在计算机中的二进制表示方式有:原码、反码和补码。

· 原码:最高位是正负符号位,其余位表示值。8 bit对应的数值范围为:[11111111,01111111],即[-127,127]。

· 反码:正数的反码和原码一致;负数的反码为最高符号位不变,其他位取反。

· 补码:正数的补码和原码一致,负数的补码在其反码的基础上加1。

既然有了原码,为什么还需要反码和补码呢?原码的最高位是符号位,这符合人的认知,但是计算机如果要区别符号位,逻辑电路就会变得复杂。因此,为了简化数字运算,要让符号位也参与到运算中。

如果直接用原码进行运算,能满足正数的运算要求,但不能满足负数的运算要求,例如:

img

如果用反码进行运算,能满足正数的运算要求,但依然不能满足负数的运算要求,例如:

img

对于0来说,区分+0和-0是没有意义的。再例如:

img

这就是反码运算中的跨0问题,必须让反码中的1111,1111和0000,0000是同一个数,才能保证运算正确。因此补码被提出,对于负数,补码是在反码的基础上加1。

补码既能满足正数的运算要求,又能满足负数的运算要求,例如:

img

1.1.4 Big-Endian与Little-Endian

在计算机世界中,整数、浮点数、双精度浮点数都用多字节来表示。计算机内存是从低地址往高地址增长,那么多字节的数字是如何在内存中排布的呢?低字节存储在低地址,称为Little-Endian;高字节存储在低地址,称为Big-Endian。以4字节整数0x01020304(十六进制)为例,用Little-Endian和Big-Endian两种方式编排的字节序列如图1-1所示。

img

图1-1 Little-Endian与Big-Endian

Little-Endian和Big-Endian来源于不同的CPU架构体系。主流的CPU架构有两大阵营,分别是PowerPC和Intel X86体系。PowerPC采用Big-Endian方式存储数据,Intel X86采用Little-Endian方式存储数据。当两种类型的机器生成的数据进行通信时,多字节序列会被翻译成不同的数值,导致不能正确地进行编码/解码。为此,计算机科学家Danny Cohen在1980年提出网络传输统一采用Big-Endian传输字节序列。因此,Big-Endian也被称为网络字节序。

对于8 bit的单个字节,也有Big-Endian和Little-Endian之分。bit序列最高位被称为MSB(Most Significant Bit),最低位被称为LSB(Least Significant Bit)。在bit序列中,Big-Endian表示最高位优先被处理,Little-Endian表示最低位优先被处理。以数字1为例,MSB到LSB的bit编排如表1-1所示。

表1-1 MSB到LSB的bit编排

img

以数字1为例,LSB到MSB的bit编排如表1-2所示。

表1-2 LSB到MSB的bit编排

img

串口通信是LSB优先发送,IIC集成电路系统总线是MSB优先发送。而现代计算机系统总线中的数据线都是多字节并行处理的,不存在bit串行发送的问题,所以不管是发送方还是接收方,都不用关心bit序列问题。