【问题标题】:Why use #define instead of a variable为什么使用#define 而不是变量
【发布时间】:2011-08-25 16:28:39
【问题描述】:

C++ 中#define 的意义何在?我只看到了使用它代替“幻数”的示例,但我没有看到将值赋给变量的意义。

【问题讨论】:

    标签: c++ c-preprocessor


    【解决方案1】:

    有一次我在工作中遇到了麻烦。我被指控在数组声明中使用“幻数”。

    像这样:

    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];
    

    在接下来的星期一下午,我被叫来并被指控有消极反抗倾向。

    【讨论】:

    • 他们说的很对。 #define MARYLYN_SIZE 256 是你想要的。
    • 重点是分开意思。 为什么 256 VS 1024 很重要?这是一个“神奇的数字”,因为没有迹象表明该值为何重要。当你知道为什么值很重要时,字面值就变得不那么重要了。
    • 然后你被解雇了,对吧?这不是一种非常专业的行为方式。你不合时宜,没有执行分配给你的简单任务。公司的政策是正确的——仅仅倾销2561024 是不可维护的,但是当遇到这个你决定让它变得更糟。如果这种行为继续发生在我的位置上,你就会出局
    • @JAMESBRYANB.Juventud 应该有多少 Marylyns 和 Anns?是什么决定推动了这一点?在该决定之后命名常量。例如static constexpr const size_t MAX_HOMES_AVAILABLE = 1024; int Ann[MAX_HOMES_AVAILABLE];。如果选择真的显而易见且已本地化,那么直接使用 1024 并没有不好,但做 Harpee 所做的只是拖钓。
    • @JAMESBRYANB.Juventud 请注意,这与可移植性没有太大关系
    【解决方案2】:

    #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。

    【讨论】:

    • 嗯,错了。 const double TAX_RATE = 0.625。 consts 默认具有翻译单元范围。
    • C 中的常量有外部链接,因此应该在 .c 文件中定义。在 C++ 中,它们具有内部链接,因此可以在标题中定义。因此,如果 C++ 而不是 C,则同意 @Neil。(问题与标题中的幻数有关,因此我不同意 #define 用于包含守卫或任何非幻数讨论的其他切线。)
    【解决方案3】:

    #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. Wikipedia article
    2. C++ specific site
    3. Documentation on GCC's preprocessor
    4. Microsoft reference
    5. C specific site(不过我认为它与 C++ 版本没有什么不同)

    【讨论】:

    • 而且,顺便说一句,现在没有充分的理由在 C++ 中使用 #define 作为常量。事实上,它被认为是非 C++ 或类似的东西。
    • 不惧怕 BOURNEGOL 的人注定要重蹈覆辙。
    • 请注意,这些都是人为的例子,只是为了展示预处理器可以做什么。我不建议任何人实际使用这些。
    • 请注意,#define fori(x) for (int i=0; i&lt;=x; i++) 应改为 #define fori(x) for (int i=0; i&lt;=(x); i++)。 (注意额外的括号。)在这个例子中括号的使用可能并不明显,但是如果有人在参数 x 中进行按位运算,就会发生错误。
    • 这应该是选择的答案。
    【解决方案4】:

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

    这里有几个使用不能被变量替换的define的例子:

    1. #define min(i, j) (((i) 请注意,这是由预处理器评估的,而不是在运行时

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

    【讨论】:

    • 定义可以很容易地被内联函数替换。
    • 该定义被预处理器“扩展”成一个 c 表达式,在编译时可能会或可能不会被评估。例如 min(1,2) vs min(a,b) 其中 a 和 b 是变量。
    • @Bo Persson:无论是否内联,都需要一个模板函数,因为它应该适用于所有类型。
    【解决方案5】:

    C 过去没有常量,所以#defines 是提供常量值的唯一方法。 C 和 C++ 现在都拥有它们,因此使用它们没有任何意义,除非它们要使用 #ifdef/ifndef 进行测试。

    【讨论】:

    • 如果您使用模板,它们仍然很方便,您需要知道编译时的值。
    • @Erbureth 常量在编译时是已知的。
    • @Erbureth:我相信命名空间范围内的 const 值默认具有内部链接,因此您也没有使用全局变量。不过,这可能只适用于整数常量。
    • 如果我进一步阅读页面下方的 cmets,我会看到这一点已经提出。哦,好吧。
    【解决方案6】:

    #define 可以完成一些普通 C++ 无法完成的工作,例如保护标头和其他任务。但是,它绝对不应该用作幻数——应该使用静态常量。

    【讨论】:

      【解决方案7】:

      这些天主要是风格。在 C 语言还很年轻的时候,还没有 const 变量这样的东西。因此,如果您使用变量而不是 #define,则无法保证某个地方的某个人不会更改它的值,从而在整个程序中造成严重破坏。

      在过去,FORTRAN 通过引用将常量传递给子例程,并且有可能(并且令人头疼)将像“2”这样的常量的值更改为不同的值。有一次,这发生在我正在处理的一个程序中,我们得到的唯一提示是,当程序遇到应该正常结束的STOP 999 时,我们会得到一个 ABEND(异常结束)。

      【讨论】:

      • @farhangdon “这些天大多是风格。”是问题的答案。基本上,如果您的风格允许,请使用它。 supercheetah's答案更好。
      • 这些天主要是风格?没有。在对可用资源有限的微控制器进行编程时,更好地使用定义来避免消耗内存的常量尤其重要。
      【解决方案8】:

      最常见的用途(除了声明常量)是include guard

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-30
        • 2015-02-19
        • 2023-01-27
        • 1970-01-01
        相关资源
        最近更新 更多