什么是计算机编程中的“幻数”?
Posted
技术标签:
【中文标题】什么是计算机编程中的“幻数”?【英文标题】:What are "magic numbers" in computer programming? 【发布时间】:2011-03-31 22:36:32 【问题描述】:当人们谈论在计算机编程中使用“幻数”时,他们的意思是什么?
【问题讨论】:
一个幻数是 42 :) 不,42 没有什么神奇之处。42 实际上 是 6 乘 9(如果您使用基数 13)。 DeepThought 和 Earth 是对的! @Victor: 那应该是THE_ANSWER_TO_THE_UNIVERSE_THE_WORLD_AND_EVERYTHING = 42
@paxdiablo - I don't write jokes in base 13.
相关帖子 - What is a magic number, and why is it bad?
【参考方案1】:
幻数是您的代码中的任何数字,对于知识很少的人来说不是立即显而易见的。
例如下面这段代码:
sz = sz + 729;
里面有一个神奇的数字,最好写成:
sz = sz + CAPACITY_INCREMENT;
一些极端观点指出,除了 -1、0 和 1 之外,您的代码中不应包含任何数字,但我更喜欢不那么教条的观点,因为我会立即识别 24、1440、86400、3.1415、2.71828 和 1.414 - 它一切都取决于您的知识。
然而,即使我知道一天有 1440 分钟,我可能仍然使用 MINS_PER_DAY
标识符,因为这样可以更轻松地搜索它们.谁说上面提到的容量增量也不会是 1440 并且您最终更改了错误的值?对于小数字来说尤其如此:37197 双重使用的机会相对较低,5 用于多个事物的机会非常高。
使用标识符意味着当容量增量发生变化时,您不必遍历所有 700 个源文件并将729
更改为730
。您可以只更改一行:
#define CAPACITY_INCREMENT 729
到:
#define CAPACITY_INCREMENT 730
并重新编译该批次。
将此与魔术常量进行对比,魔术常量是天真的人们认为仅仅因为他们从代码中删除实际数字就可以改变的结果:
x = x + 4;
到:
#define FOUR 4
x = x + FOUR;
这为您的代码添加了绝对零个额外信息,完全是浪费时间。
【讨论】:
+1 是如何不这样做的好例子(thedailywtf 昨天收集了很多这样的例子:thedailywtf.com/Articles/Avoiding-Magic-Constants.aspx) 可以,只要名字是智能的。调用常量 FOUR 只是乞求它可用于多种用途,从而重新引入易碎性:-) @paxdiablo 你为常量做了另一个很好的例子。你打错了 E 数学常数。不是:2.718128,而是:2.71828... 使用常量可以避免代码中出现(很难找到)错字。 是的,我会立即认出24
- 这是我的静态数据表的大小。或者直到昨天,无论如何,当我添加另一个项目时。现在,我将搜索并替换所有那些 24 小时...嘿 - 怎么现在一天有 25 小时???
总的来说,这是一个非常好的答案……关于魔法常数。但是“幻数”也可以指文件格式中的特殊常量、调试常量等。至少,我认为重要的是要说清楚,您所说的幻数只是幻数的一种,甚至从“编程的角度”来看。【参考方案2】:
“幻数”是出现在类似语句中的数字
if days == 365
假设您不知道一年有 365 天,您会发现这句话毫无意义。因此,最好将所有“魔术”数字(在您的程序中具有某种意义的数字)分配给一个常数,
DAYS_IN_A_YEAR = 365
从那时起,改为比较。它更容易阅读,而且如果地球被撞得不齐,而我们获得了额外的一天……您可以轻松更改它(其他数字可能更容易更改)。
【讨论】:
“如果地球被撞坏了,我们就会多一天”——奇怪,这似乎大约每四年发生一次。 :) 我发现的最有趣的一段代码:#define PI 3.14159 /* make define in case PI ever changes */
:-)
地球每四年就会失去对齐?!哇!
我记住了 250 多个位置的 pi。我每天都使用它。对了,你说的这个#define
是什么?
@Nick:让我告诉你,这真是一件令人兴奋的事情。除了每百年,我们都会幸运。 (除了每四百次,我们不这样做。)不要让我从闰秒开始。【参考方案3】:
术语幻数通常用于描述代码中的一些数字常量。这个数字没有任何进一步的描述,因此它的含义是深奥的。
使用命名常量可以避免使用幻数。
【讨论】:
【参考方案4】:除了应用程序本身之外对任何人都没有明显意义的任何东西。
if (foo == 3)
// do something
else if (foo == 4)
// delete all users
【讨论】:
【参考方案5】:在计算中使用除 0 或 1 以外的数字,这些数字不是由某个标识符或变量定义的(这不仅可以通过在一个地方更改数字来轻松在多个地方更改数字,而且还可以让读者清楚这个号码是干什么用的)。
【讨论】:
【参考方案6】:幻数是某些变量的特殊值,它会导致程序以特殊方式运行。
例如,一个通信库可能需要一个 Timeout 参数,它可以定义一个幻数“-1”来表示无限超时。
【讨论】:
【参考方案7】:***是你的朋友(Magic Number 文章)
【讨论】:
+1,用于教 OP 钓鱼(希望如此),而不仅仅是把鱼交给他。 给男人生火,他会在晚上暖和起来。让一个人着火,他的余生都会很温暖——特里·普拉切特(我想)。然而,SO 应该能够独立即使互联网的其余部分消失了! 无论如何都要链接到另一个来源,但我更喜欢在答案中加入一些肉。 Stack Overflow - 你的个人***搜索引擎!【参考方案8】:到目前为止,大多数答案都将幻数描述为不是自我描述的常数。作为一个有点“老派”的程序员,在过去我们将幻数描述为任何被分配了影响代码行为的特殊用途的常量。例如,数字 999999 或 MAX_INT 或其他完全任意的东西。
幻数的最大问题是它们的目的很容易被遗忘,或者在另一个完全合理的上下文中使用的值。
作为一个粗略且非常人为的例子:
while (int i != 99999)
DoSomeCleverCalculationBasedOnTheValueOf(i);
if (escapeConditionReached)
i = 99999;
使用或未命名常量的事实并不是真正的问题。在我这个糟糕的例子中,值会影响行为,但是如果我们需要在循环时更改“i”的值怎么办?
很明显,在上面的示例中,您不需要一个幻数来退出循环。您可以用 break 语句替换它,这是幻数的真正问题,它们是一种懒惰的编码方法,并且总是可以用不太容易失败或随着时间的推移失去意义的东西来代替。
【讨论】:
我自己更喜欢称其为哨兵值,但我知道你来自哪里。 对我来说,哨兵值存在于数据结构中——通常是一个太大(或太小)而不能成为有效键的键。但是,对于这个循环终止,我已经看到(太多次)描述为本质上优于break
,因为“break 是一个隐藏的 goto”。 goto 实现了什么? -PC = target_address;
。对我来说,这些特殊的幻数只是同一事物的变相和间接版本——将执行引导到代码中的特定点的赋值——因此通常比简单地使用 break
更不可读和可维护。
很难相信输入 break
比设置基于幻数的退出要更少懒惰。
@Steve314:我认为你找到了一个关键点。不管相对优点如何,像“break”这样的东西本质上更具可读性。我并不是说它不那么懒惰,尽管就像代码中的所有东西一样,它有它的用途。我的意思是强调,就幻数而言,代码的可维护性是一个主要问题,尤其是当您不了解它的目的时,您可能会更改或使用幻数,从而产生意想不到的副作用。
99999 是一个被使用作为标记值的幻数。【参考方案9】:
有不止一种含义。大多数答案已经给出的答案(一个任意未命名的数字)是一个非常常见的答案,我唯一要说的是,有些人走到了定义的极端......
#define ZERO 0
#define ONE 1
如果你这样做,我会追捕你,毫不留情。
不过,文件格式中使用了另一种幻数。它只是一个通常包含在文件中的值,它有助于识别文件格式、文件格式的版本和/或特定文件的字节序。
例如,您可能有一个幻数 0x12345678。如果您看到那个幻数,那么您可以合理地猜测您看到的是正确格式的文件。另一方面,如果您看到 0x78563412,则可以合理猜测您看到的是相同文件格式的字节序交换版本。
不过,术语“幻数”被滥用了一点,它指代几乎任何标识文件格式的东西 - 包括标题中很长的 ASCII 字符串。
http://en.wikipedia.org/wiki/File_format#Magic_number
【讨论】:
我认为第二种幻数(即文件格式的幻数)实际上是更常见/相关的类型。 这可能是真的——毕竟,任何优秀的程序员都会比使用幻数来指定他的幻数更清楚。 幻数的“魔力”在于它们缺乏明显的内在语义……你提到的标识符或捏造因素。【参考方案10】:简单来说,幻数是一个三位数字,其前两位数字的平方和等于第三位数字。 前 202, 如,2*2 + 0*0 = 2*2。 现在,Java 中的 WAP 接受整数并打印是否是幻数。
【讨论】:
当然不符合“从编程角度来看的幻数”的描述。【参考方案11】:这似乎有点平庸,但在每种编程语言中都至少有一个真正的幻数。
0
我认为,在几乎每个程序员的魔杖箭筒中,它都是可以统治所有这些的魔杖。
FALSE 必然是 0 TRUE 不是(FALSE),但不一定是 1!可能是 -1 (0xFFFF)
NULL必然是0(指针) 大多数编译器都允许这样做,除非他们的类型检查非常***。
0 是数组元素的基本索引,但在那些过时的语言中,基本索引为“1”。然后可以方便地为 (i = 0; i
0 是许多编程语言字符串的结尾。 “停在这里”的价值。 0 同样内置于 X86 指令中以“有效地移动字符串”。节省许多微秒。
0 经常被程序员用来表示例程执行中“没有出错”。它是“非异常”代码值。可以使用它来指示缺少抛出的异常。
程序员最常给出的答案是零是完成一些完全微不足道的工作所需的工作量,例如将活动单元格的颜色更改为紫色而不是亮粉色。 “零,伙计,就像零一样!”
0 是我们渴望实现的程序中的错误计数。 0 个未解决的异常,0 个未终止的循环,0 个无法实际采用的递归路径。 0 是我们在编程劳动、女朋友(或男朋友)“问题”、糟糕的餐厅体验和汽车的一般特性方面试图达到的渐近线。
是的,0 确实是一个神奇的数字。远比任何其他价值更神奇。没什么……咳咳,接近了。
rlynch@datalyser.com
【讨论】:
以上是关于什么是计算机编程中的“幻数”?的主要内容,如果未能解决你的问题,请参考以下文章