关于符号常量 , 恳请解答一下

2014-04-11 11:17:09 +08:00
 GPU
看 K&R 时 , 说到了符号常量 , 文中提到

[ 在程序中使用 300、20等类似的"幻数"并不是一个好习惯,它们几乎无法向以后阅读该程序的人提供什么信息,而且使程序的修改变得更加困难. ]

从而尽可能小地使用"幻数"而是使用符号常量 .

在百度百科看到这么一段话
[在c语言中,把直接使用的常数叫做幻数。在编程时,应尽量避免使用幻数,因为当常数需要改变时,要修改所有使用它的代码,工作量巨大,还可能有遗漏。因此通常把幻数定义为宏或枚举。建议使用枚举,因为它是编译阶段存在的符号,编译器的提示会更清晰、更准确。]


那么幻数我search了一下,说是一个不确定的数 , 我理解貌似就是一个广义的意思,每个领域理解都不一样 .

然后有说到 ,如果只使用幻数 会令源码作者以为的人很难知道这个幻数是做什么用的 ,
我的问题就来了 , 在定义一个数的时候也会有一个变量名啊, 怎么会很难理解呢?

然后如果是用符号变量 "#define" 也是有一个名字 , 在代码中也是和使用变量名一样的.

以上所有的 , 能否有人给一些列子让我了解一下呢 ??




==========================================================


然后 我有看到一个符号变量解释说 .
[符号常量 pi 和常变量 PI 都代表3.1415926,在程序中都能使用,只是二者的性质有所差别,定义符号常量使用#define 指令,是预编译指令,只是使用一个符号常量代表一个字符串,在预编译是仅仅进行字符替换,在预编译之后符号常量不存在了(全部换成了3.1415926),对符号常量的名字是不分配内存的。常变量要占据内存空间,有变量值,只是这个值不改变。]

这个解释说到了程序的效率 或者是性能方面的. 这个又是如何呢?




文章有点长 . 希望能有一个好点的解答啊.
3814 次点击
所在节点    程序员
28 条回复
icylogic
2014-04-11 11:21:17 +08:00
我觉得你是没搞清变量和常量的区别。。。
GPU
2014-04-11 11:23:38 +08:00
@icylogic 是完全不明白 .
icylogic
2014-04-11 11:29:22 +08:00
@GPU 3是常量,#define M 3 那m也是常量,但int a=3 a就是变量,所以你的问题其实是为什么不用变量用常量
icylogic
2014-04-11 11:30:48 +08:00
@GPU 实践中用常量的最主要的原因是,c语言只支持用常量定义数组大小。
GPU
2014-04-11 11:31:04 +08:00
@icylogic 就是这样 .不明白 . 问题还没解决.
icylogic
2014-04-11 11:31:42 +08:00
准确的说是常量表达式
jsonline
2014-04-11 11:34:03 +08:00
xia0ta0
2014-04-11 11:34:31 +08:00
幻数就是一般的数字,但是这个数字是有特定含义的,比如:
你写 int testNum = 3.14; 别人可能不是到是干啥的,你最好这样写
#define PI 3.14
float testNum = PI;
别人就知道你这是在测试圆周率,如果以后你要测得更精确,可以改一下:
#define PI 3.14159
define PI 3.14 与const float pi = 3.14;的区别就是编译之前,编译器会将所有代码有PI的地方替换成3.14,而pi只是一个变量(常量,不能修改),会编译到二进制文件中。
jsonline
2014-04-11 11:35:19 +08:00
引文:
不过,没过一会,发现了一个bug,经过大家的调查(2.6.38版没有发现这个问题),很快,找到了原因,是因为一个内存地址的问题,一个叫Yinghai Lu的人(看其名字应该是中国人,其邮件是@kernel.org)找到了原因—— radeon card使用了一个不正确的内存地址[0xa0000000 - 0xc000000]。Joerg Roedel跟贴说,这个地址超出了4GB的内存,然后他和Alex Deucher聊了一会,觉得不应该是这个问题,因为这个地址应该是GPU的,而不是系统内存的。

好像,Yinghai Lu没有理会他们说的不应该是这个问题,给出了个fix:

看到这个fix,Linus Torvalds不高兴了,他回贴问道:

为什么全都是Magic Numbers?
为什么0×80000000就那么特殊?
为什么我们这样改就行?
还说了这样一句话——


This kind of “I broke things, so now I will jiggle things randomly until they unbreak” is not acceptable. 这种“我把事搞砸了,就随意地调整直到事情又工作”的方式是不可接受的。

还说,这里即没有说明为什么我们fix在了正确的地方(也没有解释那些Magic Number是什么),也没有回滚那个有问题的patch。还说——

Don’t just make random changes. There really are only two acceptable models of development: “think and analyze” or “years and years of testing on thousands of machines”. Those two really do work.

不要乱改。那里只有两个可行的开发模式:“思考和分析” 或是 “数年数年地不断地在几千台机器上测试”。这两个方式才是真正可行的。

当然,Yinghai Lu对其做了解释,说我们的确调查过了,老的代码用的内存地址是0×80000000,新的则是用0xa0000000,而0xa0000000不工作。这又引发了 Linus Torvalds 的不满的回贴。Linus说——
jsonline
2014-04-11 11:37:30 +08:00
Magic Number 最大的问题是:
为什么是这个 number,不是另一个 number 呢?
如果有一天需求变更了,这个 number 还适用吗?

所以 Magic Number 看起来就像 Magic 一样,无法琢磨!
jsonline
2014-04-11 11:40:37 +08:00
Linus 大神每一次骂人我都看得好爽啊!
zhujinliang
2014-04-11 11:41:26 +08:00
幻数 有多层含义
1. 在代码中使用的常量,如果没有给出说明,而且他人难以推测如何得出的这个数,那就称作幻数
2. 随意指定的值,一般该值的多少对程序没有太大影响,比如 MD5 等哈希算法中的初始因子
3. 一个数值,如果得出它需要复杂的科学计算,或者通过穷举等试验得出的,可以直接写上,然后旁边写一个注释,意思是这个你不必深究了

例如:
setTimeout(fn, 86400) // 这个86400我认为是幻数,你不能一眼就知道这个指令想做什么
更好的写法是
setTimeout(fn, 24*60*60) // 一看就知道是延时一天
jsonline
2014-04-11 11:43:25 +08:00
使用常量是为了不让别人(和自己修改),所以 PI 不能作为变量来声明。这跟 Magic Number 无关。
slixurd
2014-04-11 11:51:42 +08:00
把Magic Number用在大型工程中不可取,define其实也不太好
Effective C++条款3
如果用define可能导致记号未被编译器看见,从而无法获取到编译错误信息指向的信号,只有一个Magic Number,包括调试当中,无法用记号来检索
另外用常量可能比define生成更小的代码
GPU
2014-04-11 11:55:00 +08:00
@slixurd 你的最后一句话 . 为什么把常量和 define 又扯开了呢.
mrsatangel
2014-04-11 12:00:44 +08:00
从汇编来看,就是内存操作数和立即数的区别,操作两者占用的总线周期是不一样的。当然如果不是大规模使用变量代替常量,区别可以忽略。另外赞同前面有人讲的,实践中最大的因素是c只支持常量长度的数组,不然只有动态分配。
ETiV
2014-04-11 12:05:00 +08:00
变量: 运行时可以对他进行处理的数值. 数字上来讲, 就是加减乘除.
"变量名"只是编程语言里一种对于变量的引用. 当然变量名是给人看的. 所以需要简单易懂, 望文生义.

常量: 运行时不可变而且一定不能变的量. 所以就需要对他进行保护, 以避免代码上产生的漏洞, 对"常量"进行修改.
后半句就解释了, "为什么都可以起名字, 但还非要区分变量常量"的问题.
slixurd
2014-04-11 12:18:06 +08:00
@GPU 因为常数和常量是不同的东西/w\
一个是magic number,一个是const/enum
xia0ta0
2014-04-11 12:18:59 +08:00
使用上,符号常量用PI 和常变量用pi;程序执行效率上肯定是符号常量快,因为它只是一个立即数;变常量还要操作内存。

使用变常量还是符合常量主要是从原理上考虑,如果你想把这个常量编译到程序中,就用变常量;如果只是作为一个便于记录和辨识的符合,不需要编译到二进制文件,就用符合变量。

当然,符号变量的宏定义还有很多强大的功能。
xia0ta0
2014-04-11 12:20:54 +08:00
一般习惯用大写表示符号常量,常变量用小写。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/108097

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX