为什么有人会使用 #define 来定义常量?

67

这是一个简单的问题,但为什么有人会使用#define来定义常量呢?

#define sum 1const int sum = 1;之间有什么区别呢?


30
我很想说,“因为他们是新手”……非常好的问题。 - user541686
2
是的,这个应该会带来一些有趣的答案。 - Nemo
3
@user 如果你在使用C++编程,那么非常简单--只需遵循Scott Meyer的建议Item#1“优先使用const和inline而不是#define”。仅在const无法使用时才使用#define,例如头文件包含保护等。 - greatwolf
6
为了减少得到许多不同答案的风险,请勿在标题和正文中提出不同的问题。这两个问题可能相似,但其回答方式是不同的。 - R. Martinho Fernandes
1
@Nemo 当然,他怎么能快速积累一些声望点数呢?:P - greatwolf
显示剩余5条评论
10个回答

51

#define有很多不同的应用,但你的问题似乎是关于一种特定的应用:定义命名常量。

C++中很少使用#define来定义命名常量。

#define通常在C代码中广泛使用,因为C语言在定义常量方面与C++有显著的区别。简而言之,在C中,const int对象不是常量,这意味着在C中定义真正的常量的主要方法是使用#define。(此外,对于int常量,可以使用枚举)。


1
@AndreyT 兄弟,我在这里有点害怕 :| ,这个规则是什么,这是真的答案吗,因为分数的缘故? - user788552
3
更多的回答得分并不总是意味着它是正确的。 - Synxmax
@CygnusX1:在C++中,const int前面不需要加上static。在C++中,const对象默认具有内部链接。 - AnT stands with Russia
1
@BlueRaja - Danny Pflughoeft:嗯,因为语言是这样定义的。在C语言中,您不能将“const int”对象用作“case”标签、位域大小、非VLA数组的数组大小,因为在C语言中,“const int”对象不是常量。在C语言中,“constant”一词指的是字面常量(例如“25”)和枚举成员,而不是“const”对象。 - AnT stands with Russia
@BlueRaja - Danny Pflughoeft:我在这里有一个更详细的解释https://dev59.com/THE95IYBdhLWcg3wb9hb#2308364 - AnT stands with Russia
显示剩余2条评论

46

谁都不应该不这么做!
实际上,对于许多原因,人们应该更喜欢const int sum = 1;而不是#define sum 1:

基于作用域的机制:

#define 不尊重作用域,因此无法创建类作用域名称空间。而 const 变量可以在类中进行作用域限定。


避免编译错误期间出现奇怪的魔术数字:

如果您使用#define,则这些宏将在预编译时被替换。因此,如果您在编译过程中收到错误消息,则会感到困惑,因为错误消息不会引用宏名称,而是会出现一个突然的值。您需要在代码中花费大量时间来跟踪它。


易于调试:

同样是由于第2点提到的原因,当调试时使用#define并不能真正提供帮助。


因此,为避免上述情况,使用 const 将是更好的选择。


@Martinho,如果在SO中任何答案以#开头,则会使所有内容都以大粗体显示。 - iammilind
+1 有道理,尽管这是一个好事还是坏事,我想这是有争议的。 - user541686
@Mehrdad,#define X 可以使用 #undef X 进行虚拟作用域。 - iammilind
3
笑 out loud,“为什么有人会使用 #define?”“有很多原因:1)它不好用 2)它不好用 3)它不好用。” - R. Martinho Fernandes
@Als,我希望我也能接受你的建议,感谢您提供有关编译错误和调试易用性的信息,谢谢。 - user788552
显示剩余2条评论

7

对于您刚提到的例子,我通常会使用const。当然,#define可以在其他地方用于条件编译:

#if SOME_DEFINE == 1
    // Conditional code
#endif

使用const无法做到这一点。如果不需要将值从预处理器中访问,我建议使用const,除非有某些原因不能这样做。在C++ FAQ lite中有一些相关内容,他们指出预处理器虽然“邪恶”,但这并不意味着你永远不需要它。


我不会说常量不能用于条件编译。您可以为常量专门制定一个模板。例如,请查看此处:https://dev59.com/JXI_5IYBdhLWcg3wJfkM#1505664。 - Kirill V. Lyadvinsky
是的,但我不能使用它来有条件地 #include <windows.h>,这取决于平台或类似的东西。所以我会说它不能用于某些类型的条件编译。 ;) - Sven
1
我会使用构建系统来进行这种选择,而不是使用“define”。 - Kirill V. Lyadvinsky
1
我并不会就如何进行条件编译提出任何建议,我只是指出了一种可能的使用 #define 的方法,而这种方法(使用 const)并不容易实现。 - Sven

3

#define是必要的,以使像包含守卫之类的东西起作用,因为C++没有一个真正的模块导入系统。

#define会导致对文本进行字面替换。预处理器知道如何将源代码标记化,但并不知道其中任何内容的实际含义。当您编写#define sum 1时,预处理器会遍历您的代码,并查找每个sum标记的实例,并将其替换为1标记。

这具有各种限制:#define sq(x) x * x如果像sq(3+3)这样使用,将无法正确工作;而使用#define作为常量不以任何方式尊重范围,也不会将任何类型与常量相关联。然而,#define可以(尤其是与其他一些特殊的东西结合使用,如###预处理器运算符)做一些魔法,否则不可能完成(除了手动执行预处理器所做的工作)。


我认为第一句话是一个答案,其余的内容只是OP可能已经知道的事情。 - user541686
我认为在C++中应该使用#pragma once而不是包含保护。 - Kirill V. Lyadvinsky
#pragma once 是特定于编译器的。标准没有定义预处理指令的含义。 - Karl Knechtel
但它得到了广泛的支持,通常不需要使用 #define 来实现此目的。 - Don Reba
同意,但大多数(全部?)编译器都支持它。 - Kirill V. Lyadvinsky

2

尽量使用"const int",而不是#define。

只有在您的预处理器代码可能被另一个工具读取,并且它更容易使用预处理器而不是解析语言时,请使用#define。

此外,这也是通过#if/#else/#endif定义稍后要检查的内容的唯一方式。


1
有些人错误地认为宏是确保成为“真正”的常量,并且在生成机器指令时会被原样替换。但是现代编译器会确保常量文字以相同的方式处理。
简单来说,
#define sum 1  /*is pre processed*/

这意味着,在预处理阶段完成后,sum不存在。
const int sum = 1; /*is compiled/linked*/

这意味着只要从您的程序生成可执行文件,sum 就会存在。

3
没有回答原帖中为什么要使用其中一个而不是另一个的问题。(不想太严厉,但实际上并没有回答这个问题,抱歉。)-1 - user541686
@Mehrdad,解释为什么使用#define会太长了。这就是为什么我简单地说#define是预处理的原因。 :) - iammilind

1

来自Daniel Liang所写的使用C++进行编程入门中提到:

当您使用#define指令定义常量时,该常量不会存储在内存中。该常量将被编译器替换为一个值。当您使用const关键字声明常量时,该常量像变量一样存储在内存中。

如果常量需要在多个程序中使用,请使用#define头文件中定义它,以便可以在其他程序中包含它。如果常量仅在一个程序中使用,则使用const声明更有效。


0

1
const int 不会在内存中声明一个变量;链接的文章是不正确的。 - Don Reba
@Don:你不能这样做吗?const int sum = 1; const int* sum_ptr = &sum; - R. Martinho Fernandes
1
在内存中声明一个变量,可能不够准确,无法描述为真或假。 - Karl Knechtel
1
@Martinho Fernandes: C++标准7.1.5.1.3:“指向cv限定类型的指针或引用不一定实际指向或引用cv限定对象,但它被视为实际指向或引用。”去除const属性并分配给这样的指针或引用会导致未定义的行为。 - Don Reba
重要的部分是 const int作用域 中声明一个变量。这是与使用 #define 创建常量的主要区别。是否为变量分配内存取决于编译器。 - Don Reba
显示剩余2条评论

0

const变量的反对意见已经在iammilind's answer中展示,主要是编译/链接性能方面的考虑。

通过#define定义整数常量是我观察到的老一代C开发人员和实际上是老派C开发人员或受此类人指导以非侵入方式处理旧代码库的C++开发人员中普遍存在的行为。

有趣的是,使用enum来达到相同目的的情况通常是未知的。每当我有机会时,我将相关的#defines转换为枚举。对于不属于任何组的命名常量,我使用无名称的枚举。

enum { sum = 1 };

这样,我们就可以在C和C++中使用编译时常量。


-1

const int 只是一个不能改变的 int。 #define 是一个指令,用于 C 预处理器,该预处理器不仅仅用于定义常量。

更多详情请参见这里:http://en.wikipedia.org/wiki/C_preprocessor


2
不回答为什么一个比另一个更好的问题。 (不想太苛刻,但它确实没有回答,抱歉。)-1 - user541686

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接