对于单个字节(Byte)处理器都采用相同的顺序来处理,所以单个字节的存放和传输方式一般是相同的。而对于多个字节的存放顺序,不同的操作系统就可能采用不同的方式,有的将高位字节放在内存的高地址,有的将高位字节放在内存的低地址,这就产生了小端和大端编码。

1. C 语言中的高位字节与低位字节

首先认识一下 C 语言中不同的数据结构中,哪些是高位字节哪些是地位字节

uint16_t n = 0x1234;
char t[2] = {'a', 'b'};
unsigned char* p = &n;

对于 uint16_t n = 0x1234;,其中 12 是高位字节,34 是低位字节

对于 char t[2] = {'a', 'b'},其中 a 是高位字节,b 是地位字节

对于指针 p 它指向的是n的最低地址*p 的值是与字节序有关的

2. 大端编码与小端编码

小端(Little-Endian)编码是将低位字节放在内存的低地址处,高位字节放在内存的高地址处,所以上面这个地址采用小端编码在内存中就应该是这样排列的

// 低地址 ----------> 高地址
// 0x32 12

大端(Big-Endian)编码是将高位字节放在内存的低地址处,低位字节放在内存的高地址处,所以上面这个地址采用大端编码在内存中就应该是这样排列的

// 低地址 ----------> 高地址
// 0x12 34

在了解了大端与小端编码的概念之后就可以自己来写一个程序来检测操作系统采用的是大端编码还是小端编码,首先定义一个十六进制的数字 0x0001, 然后再定义一个 unsigned char 的指针指向这个地址,由于指针所指向的为低地址,所以可以通过判断这个地址中所包含的值是否为1,如果为 1 则说明是小端编码,否则为大端编码

#include <stdio.h>
#include <stdint.h>

int isLittleEndian() {
    uint16_t num = 1;
    unsigned char *ptr = (unsigned char *)&num;
    return (int)(ptr[0]);
}

int main() {
    if (isLittleEndian()) {
        printf("Little Endian\n");
    } else {
        printf("Big Endian\n");
    }

    return 0;
}

3. 各自的优点

由于计算机都是从低地址向高地址读取,所以对于计算机而言采用小端编码效率更高,但是对应人类来说,大端编码则更符合我们的阅读习惯。

现在大多数计算机都是采用的小端编码,但是网络字节序采用的是大端编码。

4. 关于网络字节序的补充

上面提到网络字节序采用的是大端编码,所说的网络字节序指的是传输层的字节序,也就是TCP/UDP那一层的,而不是说网络上所有协议都是采用的大端编码,有一些应用层协议可能采用的就是小段编码

参考

  1. 字节序
  2. byte order
  3. what-is-the-endianness-of-captured-packet-headers