常见 CRC 算法:标准对比与选型指南

CRC 算法看着简单,选起来却挺头疼。我入行头三年,一直只用 CRC-32,觉得“反正够用”。直到有一次接手一个传感器网络项目,MCU 资源吃紧,CRC-32 算一次要几十微秒,整个循环被打乱。后来换了 CRC-8,功耗和速度都下来了。你看,选错算法真的会卡脖子。

目前业界常用的 CRC 标准有四个:CRC-8、CRC-16/CCITT、CRC-32(以太网)和 CRC-64。它们多项式不同,检错能力不同,用的地方也各不一样。下面这张核心对比表,建议你直接存下来。

标准位数多项式(十六进制)初始值输出异或典型应用
CRC-880x070x000x001-Wire、传感器、小数据包
CRC-16/CCITT160x10210xFFFF0x0000XMODEM、蓝牙、PPP
CRC-32(以太网)320x04C11DB70xFFFFFFFF0xFFFFFFFF以太网、ZIP、PKZip、GZIP
CRC-64640x42F0E1EBA9EA36930x00000000000000000x0000000000000000ECMA-182(大文件校验)

CRC-8:轻量级守卫

CRC-8 只有 8 位,算出来的校验值占一个字节。嗯,这里要注意:它的检错能力跟数据长度强相关。我以前在调试某款温湿度传感器时,发现它用 CRC-8 保护 8 字节数据,误检率大概 0.4%。对于逻辑控制类场景可以接受,但要是传几百字节,我建议你还是换个 16 位的。

  • 优点:计算量极小,1 字节开销,适合资源受限的嵌入式设备
  • 缺点:保护短数据包还好,长数据包漏检率上升
  • 常见变体:CRC-8/MAXIM(0x31)用于 1-Wire 协议,很容易搞混

CRC-16/CCITT:通信老将

我个人习惯在串口、Modbus 这类场景里优先用 CRC-16/CCITT,而不是 CRC-16 的其它变种。为什么?因为 CCITT 版的多项式 0x1021 在 HD=3 时的数据包最大长度能到 65536 位,基本够用。我记得有一次调 XMODEM 传输固件,发现接收端和发送端算的 CRC 对不上。查了半天,原来是把初始值 0xFFFF 误写成了 0x0000。你想想看,0 和 0xFFFF 差的不是一星半点。

避坑点:CRC-16 至少有七八种变体,包括 ARC、Modbus、USB 等。不确认标准就编程,99% 会踩坑。

CRC-32(以太网):业界工业标准

以太网帧、ZIP 压缩、PNG 图片……这些地方用的全是 CRC-32。说白了,它已经成了“默认选项”。我建议你在大多数通用场景下无脑选它,因为它多项式选择好、分布均匀、HD=3 时能覆盖 232-1 位数据,实际几乎覆盖所有常见数据包。但代价也很实在:32 位校验码、计算比 16 位慢一倍左右,查表法需要 1KB 的查找表。

这里给你一段 Python 的实现,看看最普通的查表 CRC-32 长什么样:

def crc32_tab():
    poly = 0x04C11DB7
    table = []
    for i in range(256):
        crc = i << 24
        for _ in range(8):
            if crc & 0x80000000:
                crc = (crc << 1) ^ poly
            else:
                crc <<= 1
        table.append(crc & 0xFFFFFFFF)
    return table

def crc32(data):
    crc = 0xFFFFFFFF
    tab = crc32_tab()
    for byte in data:
        crc = ((crc << 8) ^ tab[((crc >> 24) ^ byte) & 0xFF]) & 0xFFFFFFFF
    return crc ^ 0xFFFFFFFF
如果你用 Python 的 binascii.crc32(),注意它已经帮你做了异或 0xFFFFFFFF。但标准 C 库的很多实现需要你手动做这一步。

CRC-64:大数据时代的终极防伪

CRC-64 一般用于超大文件校验,比如文件系统、归档工具。它生成 8 字节的校验值,冲突概率极低。不过,我平时很少用它——原因很简单:大多数场景不需要。一个 2GB 的文件,CRC-32 的冲突概率也就 2-32≈1/40 亿,基本够用。只有对数据完整性要求极端严格的场合(比如金融交易快照、天文学数据)才会启用 CRC-64。

警告:CRC-64 的多项式有很多个版本(ECMA、ISO、WE 等)。我曾经在某个归档系统里因为混用了 ECMA 和 ISO 的多项式,导致同一个文件校验结果不同。一定要在技术文档里写清楚你用的是哪个标准。

选型三问

面对这些五花八门的标准,我一般问自己三个问题:

  1. 数据包最大多长? 超过几 KB 就别用 CRC-8 了,至少上 16 位。
  2. 计算资源够吗? MCU 主频低、RAM 小 → CRC-8 或 CRC-16 查表(256 字节表);PC 端 → CRC-32 随便跑。
  3. 是否需要和现有协议兼容? 比如对接 Modbus 就用 CRC-16/Modbus,不要自作主张换 CCITT。

一图看懂 CRC 家族

下面这张图帮你建立整体认知。蓝色方框是“位数”层面,红色方框是“典型应用”,橙色方框是“常见变体”,绿色方框是“注意事项”。跟着箭头走,就能快速锁定你该用哪个。

CRC 算法家族树 CRC 算法家族 CRC-8 1-Wire / 传感器 变体:MAXIM / Dallas CRC-16 Modbus / BLE / XMODEM 注意区分 CCITT / ARC / USB 初始值 ≠ 0x0000 CRC-32 以太网 / ZIP / PNG 查表法 1KB 表 多项式 0x04C11DB7 CRC-64 大文件 / 归档 多个标准不互通 谨慎选择 ECMA/ISO 选型建议:短数据 → CRC-8 / 15 | 通用通信 → CRC-16 | 万金油 → CRC-32 | 超大文件 → CRC-64

说到最后,其实每个标准都有自己的舒适区。下次你在调 CRC 的时候,不妨先看看这张表,再对着流程图走一遍。我保证,你少踩 80% 的坑。嗯,今天就聊到这儿,你先拿手头的协议比对一下看看。