校验码?校验,也需要码?【底层的世界——计算机组成原理】

《底层的世界——计算机组成原理》

校验码?校验,也需要码?【底层的世界——计算机组成原理】

5.8k 字 5 分钟 更新于 2026-10-13

这是本人系列《底层的世界 —— 计算机组成原理》的第三章 —— 校验与纠错,用于记录分享和期末复习,欢迎讨论!

引言:数据在路上跑的时候,难免被电磁干扰 “踹一脚”,0 变 1、1 变 0,好好的数据就坏了。就像你寄情书,我爱你变成了我曹你,收件人收到了那不出大事了?为了解决 “数据有没有坏” 甚至 “坏了能不能修” 的问题,人们发明了校验码 —— 往原始数据里塞点特殊的比特,让接收方能自己查错、甚至改错。

校验码?校验,还需要码?

导弹维修?导弹,还需要维修?

好了不引用雪峰老师的名言了当然是不可能的。
我是Hikari,我就在这,不紧不慢稳稳接住你。今天科普最经典的校验码:奇偶校验、海明码、CRC 循环冗余校验,从怎么来的到怎么算的,扒一扒背后的设计哲学。

奇偶校验:你验不过我你气不气

历史的必然 —— 奇偶校验

20 世纪 40 年代,第一代电子计算机和数字通信系统出现后,比特错误成为常态。哎呀怎么老被炸啊
早期的继电器、真空管器件稳定性差,数据在传输和内存读写中经常出错。工程师需要一种成本极低、计算极快的方法来快速判断 “数据有没有错”。

奇偶校验正是在这一背景下被广泛应用,它几乎不增加计算开销,只需要 1 个额外比特就能实现基础检错,成为早期计算机内存、电报通信、串口通信的标准配置。

异或的轮椅 —— 奇校验 & 偶校验

奇偶校验的核心逻辑非常朴素:统计一组二进制数据中 1 的个数,通过增加 1 位 “校验位”,让 1 的总数保持为奇数或偶数。

以原始数据 1011001(共 7 位,其中有 4 个 1)为例:

  • 偶校验:已有 4 个 1(偶数),因此校验位为 0,最终编码:1011001 0
  • 奇校验:已有 4 个 1(偶数),需要补 1 个 1 变成奇数,校验位为 1,最终编码:1011001 1

接收方收到数据后,重新统计 1 的总数,判断奇偶性是否符合约定,即可检测是否出错。
这就是奇校验和偶校验,但是光这里是没办法体现出轮椅之处,为什么轮椅呢,因为:偶校验位就是所有数据位的异或结果;奇校验位就是异或结果取反。
$$ X = d0 ⊕ d1 ⊕ ... ⊕ dn-1 $$
这不论是在硬件设计还是软件检验都是非常简单的。同时由于只有一位校验码让他冗余度极低,信息密度极高。

能力边界与设计哲学

优势:

  • 冗余度极低:n 位数据仅需 1 位校验位
  • 计算速度极快:仅需统计 1 的个数,硬件上用异或门即可实现

神也有缺点:

局限性:

  • 只能检测奇数个比特错误,无法检测偶数个比特同时翻转的情况
  • 只能检错,不能纠错 —— 知道错了,但不知道哪一位错了
  • 错误漏检率较高,仅适用于对可靠性要求不高的场景

码距与老牧师公式

这里有个老牧师公式:

$$ 2ʳ ≥ m + r + 1 $$

其中:

  • m 是数据位
  • r 是校验位

为什么能这样计算?这要开始码距的讲解了。

什么是码距(汉明距离)?

码距(Hamming Distance) 是指两个合法编码之间,对应二进制位不同的个数。

  • 最小码距 $d_{min}$:在一个编码系统中,任意两个合法码字之间码距的最小值。这是衡量编码抗干扰能力的核心指标。

你可以将最小码距 $d_{min}$想象成半径,半径内的数据他都能允许出错,半径越大,容错越高。
举例:

  • 假定数据 0000 0001 0010 … 1111为一组我们约定的合法数据,他们任何一位错误都会变成我们约定的合法数据,因此它们的码距为 1,他们不具有任何容错。
  • 假定数据 0000 0011 0101 0110 1001 1010 1100 1111为一组我们约定的合法数据,他们任何一位错误都有容错,容错位数为 1,因此它们的码距为 2。

码距与检错、纠错能力的关系

码距决定了编码能发现多少位错误,以及能纠正多少位错误(假设错误是随机的):

  1. 检测 $e$ 个错误:需要 $d_{min} \ge e + 1$
    • 如果最小码距是 2,那么随意改变 1 位(产生 1 个错误),一定会变成非法码字(被检测出来)。但如果改变 2 位,可能刚好变成另一个合法码字(无法检测)。
  2. 纠正 $t$ 个错误:需要 $d_{min} \ge 2t + 1$
    • 如果要纠正 1 位错误($t=1$),需要 $d_{min} \ge 3$。
  3. 同时检错和纠错:$d_{min} \ge e + t + 1$(其中 $e > t$)

这里可能很难理解,同样也能视作圆的半径来理解。慢慢梳理和询问请教他人和AI或许是个不错的办法。

海明码:错了!错了!哪错了?错哪了?

海明有个周末难题

1950 年,海明发表论文《Error Detecting and Error Correcting Codes》,正式奠定了纠错码理论的基础,这篇论文来自于他的周末难题。
贝尔实验室的理查德・海明快被周末的破计算机逼疯了。那时候继电器计算机老出错,一到周末没人值班,错了就直接停机,得等周一重来。
海明就吐槽:“机器都能发现错了,就不能自己改过来?” 他就开始琢磨,还真琢磨出来了 —— 不光能查错,还能精准定位到哪一位错了,直接取反就修好。
这就是海明码,人类历史上第一种能自动纠错的编码,直接把校验技术从 “检错” 卷到了 “纠错”。

校验位站 2 的幂次位

初学者的噩梦之一就是不理解为什么校验位站 2 的幂次位。
奇偶校验是一整组只做一次校验,所以只能知道错了,不知道哪错。海明码的思路很巧妙:把数据分到好几个不同的组里,每个组单独做一次奇偶校验。
就像猜数字,你问 “比 8 大吗?”“比 4 大吗?”,几个问题下来就能锁定数字。海明码就是用好几组校验的 “对错组合”,精准定位错误的位置。
还有一个举例,就像是有x,y坐标一样,给出两个位置你就能找到错误的地方了。
哪一位出错,它所在的所有组都会同时出错;反过来,哪几组错了,拼起来就是错误的位置编号。

下面介绍海明码的规则:

  • 所有位置是 2 的幂次的比特,都是校验位:第 1、2、4、8、16…… 位,记为 P₁、P₂、P₃……
  • 剩下的位置塞原始数据位,记为 D₁、D₂、D₃……
  • 第 k 个校验位,管所有二进制编号第 k 位是 1 的位置

比如:

  • P₁(第 1 位,2⁰):管 1、3、5、7、9…… 这些位置
  • P₂(第 2 位,2¹):管 2、3、6、7、10…… 这些位置
  • P₃(第 4 位,2²):管 4、5、6、7、12…… 这些位置

为什么要这么做,我们注意到注意力惊人,2 的幂次正好是像 ...0001 ...0010 ...0100 ...1000这种格式,正好能成为对应幂次的校验组。

这里以 4 位数据 1011 举例,算偶校验的海明码。

步骤 1:先占位置
由于我们要用到上面的老牧师公式,所以这里要深入理解老牧师公式。
公式本质上是在算 “状态数”够不够用 。

  • 我们总共有 $m + r$ 位(数据位+校验位)。在传输过程中,这 $m+r$ 个位置中任何一个位置都可能发生错误。
  • 加上 “没有发生错误” 这一种情况,总共有 $m + r + 1$ 种状态需要被识别。
  • 我们有 $r$ 个校验位,每个校验位算一次奇偶校验,它们组合起来可以提供 $2^r$ 种不同的校验结果(伴随式 Syndrome)。
  • 因此,$r$ 个校验位必须能够区分出这 $m + r + 1$ 种状态,即 $2^r \ge m + r + 1$。

代入数据:$m=4$。

  • 尝试 $r=2$:$2^2 = 4$,需要区分 $4+2+1 = 7$ 种状态(不够)。
  • 尝试 $r=3$:$2^3 = 8$,需要区分 $4+3+1 = 8$ 种状态(刚好够)。

所以根据老牧师公式 4 位数据需要 3 位校验位,总共 7 位。

位置 1 2 3 4 5 6 7
角色 P₁ P₂ D₁ P₃ D₂ D₃ D₄
填数据 ? ? 1 ? 0 1 1

步骤 2:挨个算校验位

  • P₁管 1、3、5、7 位:数据位有 1、0、1 → 2 个 1(偶)→ P₁=0
  • P₂管 2、3、6、7 位:数据位有 1、1、1 → 3 个 1(奇)→ P₂=1
  • P₃管 4、5、6、7 位:数据位有 0、1、1 → 2 个 1(偶)→ P₃=0

本质是做异或运算。

步骤 3:拼出最终编码
按位置填进去:0 1 1 0 0 1 1 → 海明码就是 0110011

纠错演示:
假设传的时候第 5 位翻了,收到的是 0110111。
收方重新算每组的奇偶:

  • P₁组:0,1,1,1 → 3 个 1(奇)→ 错了,记 G₁=1
  • P₂组:1,1,1,1 → 4 个 1(偶)→ 没错,记 G₂=0
  • P₃组:0,1,1,1 → 3 个 1(奇)→ 错了,记 G₃=1

把 G 拼起来:S₃S₂S₁ = 101(二进制)= 5(十进制)
所以第 5 位错了,把它取反就修好了,就是这么神奇。

海明码(Hamming Code)的默认最小码距 $d_{min} = 3$。因此,海明码能够纠正 1 位错误,或者检测出 2 位错误(纠1检2)。

海明码的优缺点

优点:

  • 能纠错单比特错误:不用重传,自己就修好,对可靠性提升巨大
  • 冗余度适中:比奇偶校验多几位,但比很多纠错码省
  • 硬件实现简单:使用异或门,电路不复杂

缺点:

  • 只能纠正 1 位错,错 2 位就只能检测不能纠正了
  • 数据短性价比不高:如 4 位数据就要 3 位校验,校验占将近一半
  • 连续好几位错的话,就只能好好享受了

现在电脑里的 ECC 内存,就是海明码的升级版本,服务器、工作站必备,就是为了防止内存比特翻转出错。

CRC 循环冗余校验:走进现代文明

前两位如果说都是 “比特级” 的小打小闹,那么 CRC 就是 “数据块级” 的工业标准。现在很多情况下你上网、插 U 盘、读硬盘,背后全是 CRC 在干活。

诞生

1961 年,MIT 的韦斯利・彼得森发表了论文,正式提出循环冗余校验的理论。
当年通信和存储发展起来了,大家要传一整块一整块的数据,奇偶校验太弱,海明码又只适合短数据。人们需要一种能对付突发错误、算得又快、还能给大块数据 “盖章” 的技术。
CRC 靠着多项式代数的数学 buff,完美踩中了所有需求,慢慢就成了全网通用的标准。
以太网、USB、SATA、蓝牙、ZIP 压缩,很多“块”级别的数据都得需要CRC。

设计原理:多项式模 2 除法

CRC 的核心思路:把一整串二进制数据当成一个多项式的系数,比如 1011 就对应 x³ + x + 1。
通信两边提前约好一个固定的 “生成多项式”,就像约定好一个印章。
发件人把数据多项式左移几位,除以这个生成多项式,得到的余数就是 CRC 校验码,附在数据后面发出去。
收件人把收到的 “数据 + CRC” 整个除以同一个生成多项式,余数是 0 就说明没坏。

这里的除法是 “模 2 除法”,也就是按位异或,加法不进位、减法不借位,0+0=0,0+1=1,1+1=0,硬件上常用移位寄存器,因此有着不错的性能。

计算方式

拿原始数据 1011001,生成多项式 1001(对应 x³+1,3 次,所以校验码 3 位)举例。

步骤 1:数据左移 r 位
原始数据后面补 3 个 0,变成被除数:1011001000

步骤 2:模 2 除法求余数
就跟小学除法竖式一样,只不过减法换成异或:

1011001000
1001        (首位是1,商1,异或除数)
----
 01000
  0000       (首位是0,商0,异或0)
  ----
  10010
  1001       (首位是1,商1,异或除数)
  ----
   00000
    0000      (首位是0,商0,异或0)
    ----
     0000
      000     (首位是0,商0,异或0)
      ----
       011    (最后剩下的余数,3位)

余数是 011,这就是 CRC 校验码。

最终发出去的就是:原始数据 + CRC → 1011001 011
收方把这一整串除以 1001,余数是 0 就说明没问题(出错的概率不高,当然你错到好处正好测不出我就给你了)。

生成多项式

常用的标准多项式有以下几个:

  • CRC-8:x⁸+x²+x+1(0x07),常用在小型嵌入式
  • CRC-16-CCITT:x¹⁶+x¹²+x⁵+1(0x1021),串口、Modem 常用
  • CRC-32:x³²+x²⁶+x²³+x²²+x¹⁶+x¹²+x¹¹+x¹⁰+x⁸+x⁷+x⁵+x⁴+x²+x+1(0x04C11DB7),以太网、文件校验全靠它

生成多项式的次数就是校验码的位数,比如 CRC-32 就是 32 位校验码,不管你数据是 1 字节还是 1GB,校验码都是固定 32 位。

CRC 的优缺点

优点:

  • 检错能力超强:能查所有单比特错、所有奇数个错、所有长度≤r 的突发错误,长突发错误也大概率能查
  • 速度极快:硬件上移位寄存器 + 异或门,流水线跑起来,多大的数据都不卡
  • 开销固定:不管数据多长,校验码就那么长,冗余效率拉满

缺点:

  • 只能检错,不能纠错(当然也有变种能纠错,但主流都是检错)
  • 数学门槛比前两个高,生成多项式不能瞎选,选不好检错能力拉胯
  • 对故意篡改没辙:这是校验不是加密,改数据同时改 CRC 就能骗过

对比

表格

对比维度 奇偶校验 海明码 CRC 循环冗余校验
核心能力 检错(仅奇数位错) 单比特纠错,双比特检错 强检错(多比特、突发错误)
冗余度 1 位 / 任意长度 约 log₂(n) 位 固定 r 位(生成多项式决定)
计算难度 极低(数 1 的个数) 中等(分组算) 低(硬件移位异或)
验错能力 单比特、奇数比特错 单比特错误为主 单比特、多比特、突发错误
硬件成本 极低 中等 低
典型应用 早期内存、串口通信 ECC 内存、嵌入式存储 以太网、USB、SATA、文件校验

现实中的应用

奇偶校验

  • 老设备串口:RS232、PS/2 键盘鼠标这些古董接口
  • 简单嵌入式:资源极度受限的 8 位单片机,偶尔还在用
  • 基础内存校验:早期的内存条奇偶校验位

海明码

  • ECC 内存:服务器、工作站、工控机的内存,防止单比特翻转
  • 嵌入式存储:NAND Flash 的页校验,很多用海明码
  • 航天级设备:对可靠性要求高,单比特错不能停机的场景

CRC

  • 计算机网络:以太网、USB、PCIe、蓝牙,几乎所有通信协议
  • 存储设备:SATA 硬盘、SSD、光盘、U 盘
  • 文件校验:压缩包、镜像文件的完整性校验
  • 工业控制:Modbus 等工业总线的通信校验

总结

奇偶校验、海明码、CRC,不是谁取代谁的关系,而是不同场景下的三种设计取舍:

奇偶校验是 “成本换够用” 的极致 —— 用最低的冗余和算力,换最基础的错误检测。它诞生于资源匮乏的年代,却在简单设备里一直活到现在。

海明码是 “结构换能力” 的智慧 —— 用分组交叉校验的设计,第一次实现了自动纠错,告诉人们冗余不光能查错,还能修错。

CRC 是 “数学换效率” 的典范 —— 用多项式代数的数学基础,实现了大块数据的高效校验,成为整个数字工业的底层标配。

理解校验码,不光是懂了几个计算机知识点,更能体会到工程里的核心哲学:没有完美的方案,只有最适合场景的权衡。用多少冗余换多少可靠性,用多少复杂度换多少能力,这就是工程师每天都在做的选择题。


小思考: 为什么你下载大文件的时候,官网经常会给一个 CRC32 或者 MD5 值让你校验?为什么网络传输用 CRC 不用海明码?这里面是效率和成本的取舍。

—

0:00
0:00