为什么要使用#define而不是变量?

80

#define 在 C++ 中的作用是什么?我只看到它被用来代替“魔法数字”,但我不明白为什么不直接将该值赋给变量。

8个回答

161

#define是C和C++预处理器语言的一部分。当它们在代码中使用时,编译器会将#define语句替换为你所需的内容。例如,如果你厌倦了一遍又一遍地写for (int i=0; i<=10; i++),可以这样写:

#define fori10 for (int i=0; i<=10; i++)

// some code...

fori10 {
    // do stuff to i
}

如果你想要更通用的东西,你可以创建预处理器宏:

#define fori(x) for (int i=0; i<=x; i++)
// the x will be replaced by what ever is put into the parenthesis, such as
// 20 here
fori(20) {
    // do more stuff to i
}

如果您只想在某个特定构建中使用某些代码,那么宏定义(#define)还非常有用,这也是条件编译的另一个主要用途:

// compile the following if debugging is turned on and defined
#ifdef DEBUG
// some code
#endif

大多数编译器允许您从命令行定义宏(例如 g++ -DDEBUG something.cpp),但您也可以在代码中像这样定义:

#define DEBUG

一些资源:

  1. 维基百科文章
  2. C++ 特定网站
  3. GCC 预处理器文档
  4. 微软参考文献
  5. C 特定网站(我认为它与 C++ 版本没有区别)

10
顺便说一下,现在没有充分的理由在C++中使用#define定义常量。事实上,这被认为是违反C++规范的做法或者类似于这样的做法。 - supercheetah
3
那些不害怕 BOURNEGOL 的人注定要重蹈覆辙。 - Paul Tomblin
6
这只是一个说明,这些例子都是非常牵强附会的,仅用于展示预处理器的功能。我不建议任何人实际使用它们。 - supercheetah
那些宏最好是大写的,记得微软公司的“max”和“min”的惨败。 - Slava
9
请注意,#define fori(x) for (int i=0; i<=x; i++) 应该改为 #define fori(x) for (int i=0; i<=(x); i++)。 (请注意额外的括号。)在这个例子中,括号的使用可能不明显,但是如果有人在参数 x 中放置位运算,将会发生错误。 - lwchkg
1
这应该是被选中的答案。 - frankliuao

60

现在大多数情况下是为了风格而使用const。当C语言年轻的时候,没有const变量这个概念。因此,如果你使用一个变量而不是#define,你无法保证某个地方的某个人不会更改它的值,从而在整个程序中造成混乱。

在旧时代,FORTRAN甚至将常量作为引用传递给子程序,这可能会导致将像“2”这样的常量的值更改为其他值(这会让人头疼)。有一次,在我正在工作的程序中发生了这种情况,我们唯一的提示是当程序达到本应正常结束的STOP 999时,我们会收到一个ABEND(异常结束)。


13
“现在大多数情况下这只是一种风格。” 这是回答这个问题的答案。基本上,如果您的风格允许,请使用它。supercheetah 的回答更好。 - Trisped
3
现在大多数情况下都是风格上的吗?不是的。最好使用定义来避免消耗内存的常量,特别是在使用资源有限的微控制器进行编程时更为重要。 - Codebeat

38

有一次我在工作中遇到了麻烦。有人指责我在数组声明中使用了“魔法数字”。

就像这样:

int Marylyn[256], Ann[1024];

公司的政策是避免使用这些“魔法数”,因为据我解释,这些数字不具有可移植性,会阻碍易于维护。我曾争辩说,在阅读代码时,我想精确知道数组的大小。但我最终失败了,于是在一个周五下午,我将这些有问题的“魔法数”替换为像这样的#defines:

 #define TWO_FIFTY_SIX 256
 #define TEN_TWENTY_FOUR 1024

 int Marylyn[TWO_FIFTY_SIX], Ann[TEN_TWENTY_FOUR];

接下来的星期一下午,我被叫去并被指控具有被动抵抗倾向。


23
他们非常正确。#define MARYLYN_SIZE 256 就是你想要的。 - Mo Beigi
5
重点是分离意义。为什么256与1024很重要?这是一个“魔法数字”,因为没有说明该值的重要性。当你知道该值为何重要时,字面上的数值就不那么重要了。 - Dave Cousineau
7
然后你被解雇了,对吧?这种行为方式并不专业。你的行为越界了,没有完成分配给你的简单任务。公司的政策是正确的——仅仅把“256”和“1024”倒进去是不能维护的,然而当面对这个问题时,你决定让事情变得更糟。如果在我的公司里继续这样做,你会被辞退的。 - Lightness Races in Orbit
1
@JAMESBRYANB.Juventud 应该有多少个Marylyns和Anns?是什么决定了这一点?在那个决定之后命名常量。例如:static constexpr const size_t MAX_HOMES_AVAILABLE = 1024; int Ann[MAX_HOMES_AVAILABLE];。如果选择非常明显且本地化,那么直接使用1024并不会太糟糕,但像Harpee所做的那样就是在刁难人了。 - Lightness Races in Orbit
1
@JAMESBRYANB.Juventud 注意,这并不与可移植性有太大关系。 - Lightness Races in Orbit
显示剩余7条评论

8

#define可以完成一些普通的C++无法完成的任务,比如保护头文件和其他任务。然而,它绝对不应该被用作魔数-应该使用静态常量代替。


7

C语言以前没有常量,因此#define是提供常量值的唯一方法。现在C和C++都有常量,因此除非它们将使用#ifdef/ifndef进行测试,否则没有使用它们的必要。


如果你使用模板,那么它们仍然很方便,因为你需要在编译时知道值。 - Erbureth
2
@Erbureth 常量在编译时已知。 - user2100815
2
@Erbureth:我相信命名空间作用域中的const值默认具有内部链接,因此您也不使用全局变量。虽然这可能仅适用于整数常量。 - Dennis Zickefoose
2
如果我读了页面下面的评论,我就会看到这一点已经被提出了。哦,好吧。 - Dennis Zickefoose

4

最常见的用途(除了声明常量之外)是 包含保护


1

预处理器在编译之前评估define,而变量是在运行时引用的。这意味着您可以控制应用程序的构建方式(而不是运行方式)

以下是一些使用define的示例,它们无法被变量替换:

  1. #define min(i, j) (((i) < (j)) ? (i) : (j))
    请注意,这是由预处理器在编译期间评估的,而不是在运行时。

  2. http://msdn.microsoft.com/en-us/library/8fskxacy.aspx


3
定义可以很容易地被内联函数替代。 - Bo Persson
1
该定义被预处理器“展开”为一个C表达式,可能在编译时进行评估,也可能不进行评估。例如min(1,2)与min(a,b),其中a和b是变量。 - Sil
@Bo Persson:需要一个模板函数,无论是否内联,因为它应该适用于所有类型。 - Robin Hsu

0

#define 允许您在头文件中设置一个值,否则会编译成大于零的大小。您的头文件不应该编译成大于零的大小。

// File:  MyFile.h

// This header will compile to size-zero.
#define TAX_RATE 0.625

// NO:  static const double TAX_RATE = 0.625;
// NO:  extern const double TAX_RATE;  // WHAT IS THE VALUE?

编辑:正如Neil在本帖的评论中指出的那样,头文件中的显式定义(带值)适用于C++,但不适用于C。


2
不对。const double TAX_RATE = 0.625。默认情况下,常量具有翻译单元作用域。 - user2100815
在C语言中,常量具有外部链接性,因此应该在.c文件中定义。在C++中,它们具有内部链接性,因此可以在头文件中定义。因此,如果是C++而不是C,请同意@Neil的观点。(该问题涉及头文件中的魔数,因此我不同意其他人关于#define用于包含保护或任何非魔数讨论的离题讨论。) - charley

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