编程中的「魔数」(magic number)是什么意思?平时我们能接触到哪些魔数?

关注者
564
被浏览
721,621

42 个回答

“魔数”有贬义词、中性词、褒义词三种用法,默认为贬义词。

1、贬义词“魔数”

指的是代码中出现的没有说明的数字。代码中突然出现一个没说明用途的数字会让其它阅读代码、维护代码的的人非常难受。例如:

const int N = 2073600;
for (int i=0; i<N; i++)
 { ......

看代码的人需要猜2073600是什么意思,而且特别难猜。改成这样就清楚多了:

const int WIDTH = 1920;
const int HEIGHT = 1080;
int totalPixels = WIDTH * HEIGHT;

这里的“魔数”就是指代码中直接出现的数字。现代编程规范比较忌讳这样写代码,一方面看不懂意思,另一方面如果2073600这个数字多次出现,一旦需要修改的时候就需要全部找出来改掉,一旦少改一处就会产生BUG,非常麻烦。

我们在编程中要尽可能避免使用“魔数”,例如写3.1416这种数字,也应该改为数学库中的π常数,例如Unity中的Mathf.PI

2、褒义词“魔数”

最典型的例子就是现代3D游戏之父约翰·卡马克在雷神之锤中的那个魔数:

i = 0x5f3759df - ( i >> 1 );

配合前后的代码,这句代码可以快速计算一个数字的平方根的倒数。具体推导过程比较复杂,涉及到浮点数的原理。

这个靓丽的骚操作给了其它人非常大的震撼,是那个年代的大神级编程技术的缩影,所以被人们怀着敬仰之情称之为“魔数”,全称为“如魔法一样神奇的数字”。

3、中性词“魔数”

某些具有特定格式的文件,喜欢在文件开头写几个特殊的字符以表明自己的身份,以便验明正身。例如常见的几种图片格式的文件:

  1. JPEG (jpg),文件头:FFD8FF
  2. PNG (png),文件头:89504E47
  3. GIF (gif),文件头:47494638
  4. Windows Bitmap (bmp),文件头:424D

如果你用16进制编辑器打开一个文件,它的开头不是FFD8FF,那就不是jpg文件。这个魔数一般会在相关文件标准中进行规定,所有人都要遵守。

其它答主也列举了多种“魔数”,我再补充一个常用的魔数——Unicode的BOM魔数。由于它在Windows记事本中也会出现,所以我们会经常接触到它,虽然没有感觉。

打开一个记事本,随便输入一些内容,然后另存为。另存的时候可以选择编码

  1. 如果保存为Unicode,文件开头就会出现FF FE。
  2. 如果保存为Unicode big endian,文件开头就会出现FE FF。
  3. 如果保存为UTF-8,文件开头会出现EB BB BF。

这些数字不仅能代表它是什么编码,还能指定读取编码的顺序。细节就不谈了。

说这个例子是因为Windows记事本曾经出现过一个非常神奇的bug——你无法以ANSI编码保存一个内容为“联通”的文本文件。

这个问题是因为ANSI编码保存时,开头没有“魔数”,导致记事本识别不出文件是什么编码。而且“联通”两个字的GBK编码容易被错误识别为UTF-8编码,所以出了问题。

这个小BUG曾被作为微软和电信私通打击联通的证据 。

Magic number一般是指硬写到代码里的整数常量,数值是编程者自己指定的,其他人不知道数值有什么具体意义,表示不明觉厉,就称作magic number。

编程教材书用magic number指代初学者不定义常量直接写数的不良习惯。

例子:

ELF文件头会写入一个magic number,检查这个数和自己预想的是否一致可以判断文件是否损坏。

在缓冲区数组旁放一个magic number(称作canary金丝雀),通过检查是否一致可检测缓冲区溢出攻击。