
时间回到2024年7月,美国网络安全公司CrowdStrike的安全软件更新引发的全球 Windows 设备蓝屏事件,该事件波及数十个国家,航空、金融、医疗等关键行业受创严重,出现航班停飞、金融交易中断、医疗活动停滞等情况。事件造成的潜在经济损失接近 1000 亿美元;且影响持续时间长,事发 5 天后仍有 3% 的蓝屏设备未恢复,直至第 10 天左右才基本恢复正常。谁曾想到,这一灾难性的事件仅仅源于它的代码试图去读取一个长度为20的数组中的第21个值。这就像是一个只有20个格子的文件柜,代码却非要去拿第21个格子里的东西,但这个格子根本不存在,一伸手就把整个柜子掀翻了。这也就是差一错误(Off-by-one error)。
差一错误源于现代计算机语言默认使用的零索引写法,这就要求程序员总是从0开始数数,然而人们也习惯于从1开始数数。很多学习编程的人都会有一个疑问,为什么索引是从0开始的?
从早期的 C 语言开始,数组在内存中是连续存储的,而所谓索引,本质上代表的是 “相对于数组起始地址的偏移量”。这里的 “0”,意思就是 “没有偏移”,直接对应数组第一个元素的起始位置。这样一来,计算任意元素的内存地址时,只需用数组起始地址 + 索引 × 单个元素长度就能得出,不需要额外加1,运算效率因此更高。但是后来发明了其它编程语言的人,都开在数组的开头放入大量的元数据(Metadata,用于描述数据的数据),并且他们还使用了各种新型的数据结构,这些新型数据结构往往不会把数据放在一个内存片区,从而提高内存的利用率和操作效率。因此,这些语言的数组也不再依赖于“数组起始地址的偏移量”,而开始使用了真正的索引。让人费解的是,索引完全可以从1开始数,但这些编程语言的发明者却莫名其妙地延续了从0开始的数数方式。
这场千亿损失的灾难,本质是人性与规则的错位。编程语言发明者早已熟稔零索引逻辑,甚少出错,但普通程序员难逃 “从 1 计数” 的直觉本能,再加上认知偏差与熟悉盲点,差一错误便成了高频 “人性 Bug”。