2. 数据表示与编码:大端与小端、ASCII与二进制编码、BCD码与自定义编码规则

协议解析说白了就是「读懂对方发来的字节」。但字节本身不会说话,你得先搞清楚它怎么排列、用什么编码。我见过太多工程师在字节序上翻车,一查就是半天。这一章咱们就把这些基础打牢。

核心要点:数据表示决定了你怎么解释收到的 0 和 1。选错编码,数据就是一堆乱码。

2.1 大端 vs 小端:字节序的江湖

先问个问题:一个 16 位的整数 0x1234,在内存里怎么放?

大端(Big-Endian)把高位字节放低地址,小端(Little-Endian)把低位字节放低地址。举个例子:

  • 大端存储:地址 0x00 存 0x12,地址 0x01 存 0x34
  • 小端存储:地址 0x00 存 0x34,地址 0x01 存 0x12

我个人习惯把大端想象成「从左往右读」,小端是「从右往左读」。你想想看,网络协议几乎清一色用大端,而 x86 处理器天生就是小端。我在项目中遇到过两次因为字节序没对齐,导致设备间通信数据全反了——排查了整整一个下午。

特性 大端(Big-Endian) 小端(Little-Endian)
高位字节位置 低地址 高地址
常见场景 网络协议、TCP/IP、Modbus x86/ARM 处理器、USB、PCIe
人类阅读友好度 直观,和书写顺序一致 反直觉,但硬件实现简单

实用技巧:写嵌入式代码时,用 htons()、htonl() 做主机到网络字节序转换。别自己手写移位,容易出错。

2.2 ASCII 与二进制编码

ASCII 是最基础的字符编码,一个字节表示一个字符。比如 'A' 是 0x41,'0' 是 0x30。但注意,ASCII 只覆盖了 128 个字符,遇到中文就抓瞎了。

二进制编码更直接——你想传什么值,就直接把它的二进制发出去。比如要传整数 255,ASCII 需要三个字节 '2' '5' '5'(即 0x32 0x35 0x35),而二进制编码只需要一个字节 0xFF。

我记得有一次做传感器数据采集,对方协议文档写的是「ASCII 编码传输」。结果每帧数据 200 多个字节,解析起来慢得要命。后来改成二进制编码,帧长直接砍到 50 字节以内。嗯,这里要注意:二进制编码虽然省空间,但调试时肉眼看不出来,得用工具辅助。

避坑指南:我曾经在调试串口时,发现收到的数据总是多一个 0x0D(回车符)。查了半天,原来是终端软件把 0x0A(换行)自动转换成了 0x0D 0x0A。ASCII 里的控制字符在传输中经常被「好心」修改,务必小心。

2.3 BCD 码:用半个字节表示一个数字

BCD(Binary-Coded Decimal)码用 4 位二进制表示一个十进制数字。比如十进制 95,BCD 码就是 1001 0101(即 0x95)。

为什么不用纯二进制?因为 BCD 码没有进位误差,适合金融、计时等场景。比如 RTC 芯片里的时间寄存器,几乎清一色用 BCD 码。

十进制 二进制 BCD 码
12 0000 1100 0001 0010
99 0110 0011 1001 1001
255 1111 1111 0010 0101 0101

BCD 码的转换也很简单:高位 BCD 乘以 10 再加低位 BCD。比如 0x95 转十进制:9 * 10 + 5 = 95。反过来,十进制转 BCD 就是不断除以 10 取余数。

实用技巧:解析 RTC 时间时,记得把 BCD 转成整数再比较。我见过有人直接拿 BCD 值做大小判断,结果 0x59(59秒)比 0x01(1秒)还小——因为 0x59 的数值是 89,确实比 1 大,但 BCD 的 59 就是 59 秒。别搞混了。

2.4 自定义编码规则:灵活但要有章法

有时候标准编码不够用,就得自己定义。比如工业协议里常见的「压缩 BCD」:用 3 个字节存 5 个十进制数字,省一个 nibble。或者用 7 位编码代替 8 位 ASCII,省带宽。

自定义编码的核心原则:

  • 明确边界:每个字段占多少位,从哪开始,到哪结束
  • 保留扩展:留几个保留位,方便以后加功能
  • 文档化:编码规则必须写清楚,不然别人看不懂

我在项目中做过一个自定义编码:用 4 个字节表示一个带符号的定点数,前 2 字节是整数部分,后 2 字节是小数部分。这样传输效率高,而且精度可控。但后来维护的人换了三茬,每次都要翻文档才能看懂。所以自定义编码一定要配套文档,别偷懒。

经验之谈:能用标准编码就别自己造轮子。实在要自定义,务必在协议头里加一个「编码类型标识符」,方便后续兼容。

2.5 本章知识体系

下面这张图把本章的核心知识点串起来了。你可以把它当作一个快速索引:

数据表示与编码知识体系 数据表示与编码知识体系 数据编码 字节序 字符与数值编码 自定义编码 大端 小端 ASCII 二进制 BCD码 压缩BCD 7位编码 核心要点 • 大端:网络协议标准,高位字节在前 • 小端:x86/ARM 原生格式,低位字节在前 • ASCII:人类可读,但传输效率低 • BCD:无进位误差,适合金融/计时 • 自定义编码:灵活但必须文档化

这张图把字节序、字符编码、数值编码和自定义编码的关系理清楚了。实际工作中,你往往需要组合使用它们。比如一个 Modbus 帧,地址用二进制编码,寄存器值用大端,时间戳用 BCD——你得全部吃透才能正确解析。

最后提醒:协议解析的第一步永远是「确认字节序和编码方式」。别上来就写解析代码,先看文档,或者用抓包工具确认一下。我曾经因为没确认编码方式,浪费了三天时间——结果只是对方用了小端,而我默认是大端。