软考备考笔记(四):校验码

8 阅读8分钟

起点:数据传输是会出错的

数据从 A 传到 B,中间经过线路、网络、磁盘,任何一个环节都可能让某一位翻转(0 变 1 或 1 变 0)。

A 发送:1 0 1 1 0 1 0 0
              ↓
        传输过程中被干扰
              ↓
B 收到:1 0 1 1 1 1 0 0
                    ↑
                 这一位变了

B 收到的数据和 A 发的不一样,但 B 不知道。如果直接用这个错误数据,程序就会出 bug,文件就会损坏。

所以需要一种机制,让 B 能发现数据有没有错。

这就是校验码要解决的问题。


三种校验码,解决的问题逐步升级

奇偶校验 → 能发现错误,但不知道哪位错了
海明码   → 能发现错误,而且知道哪位错了,能纠正
CRC      → 能发现错误,检错能力最强,但不能纠正

它们之间不是替代关系,而是针对不同场景的不同工具。


一、奇偶校验

原理

在数据末尾加 1 位,让整个数据中 1 的个数是偶数(偶校验)或奇数(奇校验)

例子

数据 1011001,偶校验:

数一下 1 的个数:1+0+1+1+0+0+1 = 4 个(偶数)
偶校验要求偶数,已经满足,所以校验位 = 0

发送:1011001 0
              ↑ 校验位

数据 1011011,偶校验:

数一下 1 的个数:1+0+1+1+0+1+1 = 5 个(奇数)
偶校验要求偶数,不满足,所以校验位 = 1(补一个,变成 6 个)

发送:1011011 1
              ↑ 校验位

接收方怎么检查

收到数据后,数 1 的个数:

偶数个 1 → 没错
奇数个 1 → 出错了

能力和局限

能检测:1 位错误(1 位翻转,奇偶性一定变)
能检测:3 位、5 位...(奇数个位翻转,奇偶性一定变)

不能检测:2 位错误(两个位同时翻转,奇偶性不变,互相抵消)
不能纠正:知道错了,但不知道哪位错

一句话总结

奇偶校验就是给数据加一个"计数校验",1 的个数对不上就是错了。简单,但能力有限。


二、海明码

为什么需要它

奇偶校验只能告诉你"错了",不能告诉你"哪位错了"。

如果能知道哪位错了,就能直接纠正——把那一位取反就行。

海明码的目标:用多个校验位,精确锁定出错位置。

核心思路

用多个校验位,每个校验位检查数据的不同子集。出错时,把所有"报错"的校验位编号拼起来,就是出错位置。

第一步:校验位放在哪,需要几个

校验位放在位置编号是 2 的幂的地方:位置 1、2、4、8、16...

需要几个校验位取决于数据长度:

2^r ≥ 数据位数 + 校验位数 + 1

以 4 位数据为例,需要 3 个校验位(2³ = 8 ≥ 7+1):

位置:  1     2     3     4     5     6     7
内容:  PP₂    D₁    P₃    D₂    D₃    D₄

第二步:每个校验位管哪些位置

规则:把位置编号转成二进制,第 k 位是 1 的位置就归 Pₖ 管。

位置 1 = 001 → 归 P₁
位置 2 = 010 → 归 P₂
位置 3 = 011 → 归 P₁、P₂
位置 4 = 100 → 归 P₃
位置 5 = 101 → 归 P₁、P₃
位置 6 = 110 → 归 P₂、P₃
位置 7 = 111 → 归 P₁、P₂、P₃

整理:

P₁ 管:1, 3, 5, 7
P₂ 管:2, 3, 6, 7
P₃ 管:4, 5, 6, 7

不管数据多长,规则永远是这一个:Pₖ 管二进制第 k 位是 1 的所有位置。

第三步:发送方填校验位

数据 D₁=1, D₂=0, D₃=1, D₄=1,先填进去:

位置:  1     2     3     4     5     6     7
内容:  PP1    P0     1     1

规则:每个校验位管的那组位置中,1 的个数必须是偶数。

P₁ 管位置 1,3,5,7 → 位置3=1, 位置5=0, 位置7=121(偶数)→ P₁ = 0
P₂ 管位置 2,3,6,7 → 位置3=1, 位置6=1, 位置7=131(奇数)→ P₂ = 1
P₃ 管位置 4,5,6,7 → 位置5=0, 位置6=1, 位置7=121(偶数)→ P₃ = 0

发送出去:

位置:  1     2     3     4     5     6     7
内容:  0     1     1     0     0     1     1

第四步:接收方怎么定位错误

收到数据后,重新检查每组的奇偶性:

P₁ 组(位置1,3,5,7):1 的个数是偶数 → 没问题 → S₁ = 0
P₂ 组(位置2,3,6,7):1 的个数是奇数 → 有问题 → S₂ = 1
P₃ 组(位置4,5,6,7):1 的个数是奇数 → 有问题 → S₃ = 1

把结果拼起来:

S₃S₂S₁ = 110 = 6 → 位置 6 出错

把位置 6 取反,纠正完成。

为什么拼二进制就能定位

因为校验位的分组规则就是按二进制设计的:

S₁ = 1 → 出错位置的二进制第 1 位是 1
S₂ = 1 → 出错位置的二进制第 2 位是 1
S₃ = 0 → 出错位置的二进制第 3 位是 0

拼起来:110 = 位置 6

分组规则和定位规则是配套设计的,不是巧合。

能力和局限

能检测:1 位错误
能纠正:1 位错误(精确定位,取反纠正)

不能纠正:2 位同时出错(定位会指向一个错误的位置)

一句话总结

海明码用多个校验位按二进制分组,每组检查奇偶性,报错的组拼起来就是出错位置。能纠正 1 位错误。


三、CRC 循环冗余校验

为什么需要它

奇偶校验和海明码都有局限:

奇偶校验:2 位同时出错就检测不到
海明码:只能纠正 1 位错误

网络传输、磁盘存储这些场景中,经常会出现连续多位出错(比如信号突然中断一小段)。需要一种检错能力更强的方法。

CRC 的目标:不管错了 1 位还是多位,都能检测出来。

核心思路

发送方和接收方约定一个除数(叫生成多项式)。

发送方用数据除以这个除数,把余数附在数据后面一起发出去。

接收方收到后,用同样的除数再除一次:

余数为 0 → 数据没变 → 没错
余数不为 0 → 数据变了 → 出错

什么是模 2 除法

模 2 除法就是每一位做异或(XOR),没有进位和借位。

XOR 规则:
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0

简单说就是:两位相同为 0,不同为 1。

完整过程

假设数据是 1101,约定的除数是 1011(4 位)。

发送方:

第一步:数据后面补零,补的个数 = 除数位数 - 1 = 3

1101 000

第二步:用模 2 除法除以 1011

         1 1 1 0
        ────────
1011 ) 1 1 0 1 0 0 0
       1 0 1 1
       ───────
         1 1 0 0
         1 0 1 1
         ───────
           1 1 1 0
           1 0 1 1
           ───────
             1 0 1 0
             1 0 1 1
             ───────
               0 0 1 0 ← 余数 = 010

每一步都是 XOR:比如 1101 XOR 1011 = 0110

第三步:把余数 010 替换掉后面的零

发送:1101 010
           ↑
        余数(校验码)

接收方:

收到 1101010,用同一个除数 1011 去除:

余数 = 000 → 没错 ✓

如果传输中任何一位变了,比如变成 1111010

 1011 去除  余数  000  出错 

为什么能检测多位错误

发送的数据一定整除(因为余数是我们自己填上去的)。

传输中任何一位变了,相当于数据被翻转了。翻转后的数据除以同一个除数,余数几乎不可能是 0

这是因为除数(生成多项式)是精心选择的数学对象,它保证了:

任何 1 位翻转 → 余数 ≠ 0
任何连续 ≤ n 位翻转 → 余数 ≠ 0(n = 校验码位数)
任何奇数个位翻转 → 余数 ≠ 0

这不是靠人去检查的,是数学性质保证的。就像一把钥匙开一把锁,钥匙上任何一个齿磨掉一点就打不开。

能力和局限

检错能力很强:能检测多位错误、突发错误
不能纠正:知道错了,但不知道哪位错

一句话总结

CRC 用数学除法算出一个校验码附在数据后面,接收方重新除一次,余数不为零就是错了。检错能力最强,但不能纠正。


三种校验码对比

奇偶校验海明码CRC
校验位数量1 位多位多位
能检测 1 位错误
能检测多位错误不能不能
能纠正错误不能能纠正 1 位不能
底层原理数 1 的个数分组奇偶 + 二进制定位多项式除法
适用场景简单传输内存(ECC)网络、磁盘

三者的关系

奇偶校验:1 个校验位,检查整体奇偶性
   ↓ 能力不够,想知道哪位错了
海明码:多个校验位,按二进制分组,定位错误
   ↓ 能力不够,想检测多位错误
CRC:用数学除法,检错能力最强

它们不是互相替代,而是各有适用场景。计算机系统中经常组合使用。