17 大小端与协议字节序

核心记忆:小端低字节先放;大端高字节先放。以协议规定为准,不以 CPU 字节序为准。

1. 一张图看懂

16 位数 0x1234 分成高字节 0x12 与低字节 0x34。下图从左到右表示地址由低到高。

                   低地址 ─────────→ 高地址
小端 Little Endian   ┌─────────┬─────────┐
                     │  0x34   │  0x12   │
                     │ 低字节  │ 高字节  │
                     └─────────┴─────────┘

大端 Big Endian      ┌─────────┬─────────┐
                     │  0x12   │  0x34   │
                     │ 高字节  │ 低字节  │
                     └─────────┴─────────┘
对比小端(Little Endian)大端(Big Endian)
低地址处低有效字节高有效字节
数值 0x1234 的传输/存储顺序34 1212 34
两字节解析第 2 字节左移 8 位第 1 字节左移 8 位

2. 代码:两个字节怎么拼成 uint16_t

假设 buf 已包含两个有效字节:

uint8_t buf[2] = {0x12, 0x34};

uint16_t le = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8); // 0x3412
uint16_t be = ((uint16_t)buf[0] << 8) | (uint16_t)buf[1]; // 0x1234

看谁左移 8 位,就是谁放到高字节。 这种显式移位拼接方法不依赖 CPU 本机的大小端。

3. 蓝牙/TWS:命令包示意

某自定义协议示例(并非通用蓝牙命令格式):命令号 1 字节 + 小端参数 2 字节。

字节下标      buf[0]          buf[1]          buf[2]
             ┌───────────┬───────────┬───────────┐
命令内容     │   0x01    │   0x34    │   0x12    │
             └───────────┴───────────┴───────────┘
含义            命令号       参数低字节      参数高字节
                                      └──→ value = 0x1234
uint8_t cmd[3] = {0x01, 0x34, 0x12};
uint16_t value = (uint16_t)cmd[1] | ((uint16_t)cmd[2] << 8);
// value == 0x1234

4. 最后记忆

  • 小端:先低后高。大端:先高后低。
  • 字节序规定的是多字节整数的字节排列顺序,不是数值本身发生改变。
  • 先看协议文档,再决定拼接顺序;不要直接按 CPU 大小端猜测报文格式。
  • 本节只讨论字节序;长度和边界检查见第 15 篇。