【问题标题】:Is #ifdef MACRO equivalent to a comment#ifdef MACRO 是否等同于注释
【发布时间】:2013-06-10 20:57:17
【问题描述】:

假设没有定义 MACRO,这些是等价的

#ifdef MACRO
    Not valid C or C++ code
#endif

/*
    Not valid C or C++ code
*/

在 GCC 4.7.1 中,它似乎是等价的,但有没有做更多的预处理器?

【问题讨论】:

  • 如果MACRO is not defined然后YES,代码在这两种情况下都不会编译,预处理器用于条件编译,如果定义为true,则代码编译否则不

标签: c comments c-preprocessor conditional-compilation


【解决方案1】:

这取决于您所说的“无效的 C 或 C++ 代码”是什么意思。

comment 中的文本不必符合大多数语言规则。它甚至没有被标记化。这是完全有效的:

/* This comment doesn't contain a valid sequence of preprocessing tokens
   (because of the apostrophe).  */

它必须遵守的唯一规则是控制评论结束位置的规则。人们经常被行 cmets 中的反斜杠换行符绊倒(事实上,SO 的语法高亮器曾经弄错了!)

// Line comment with ascii art ending with a \
   Oops! This line is commented out too!

块 cmets 不嵌套的频率较低(如果只是因为每个 C 教程都会警告您):

/* you can't nest /* block comments */ these words are not commented */

另一方面,“跳过”预处理器条件“组”中的文本确实必须符合语言的某些规则。标准(C99 §6.10.1p5)的确切内容是

按顺序检查每个指令的条件。如果它评估为假(零),则该组 它控制的被跳过:指令仅通过确定的名称来处理 该指令用于跟踪嵌套条件的级别;剩下的 指令的预处理标记被忽略,其他预处理标记也是如此 组。

有两个重要的部分。首先,文本标记化的,所以它确实必须是一个有效的预处理标记序列。

#if 0
This skipped conditional group doesn't contain a valid sequence of
preprocessing tokens (because of the apostrophe).
#endif

是语法错误。

$ gcc -fsyntax-only test.c
test.c:2:37: warning: missing terminating ' character
 this skipped conditional group doesn't contain a valid sequence of
                                     ^

其次,指令仍被部分处理“以跟踪嵌套条件的级别”,这意味着您可以这样做:

#if 0 // forget this entire mess
    #ifdef __linux__
    do_linux_specific_thing();
    #elif defined __APPLE__
    do_osx_specific_thing();
    #elif defined _WIN32
    do_windows_specific_thing();
    #endif
#endif

而你不能这个

    #ifdef __linux__
    do_linux_specific_thing();
    #elif defined __APPLE__
    do_osx_specific_thing();
#if 0 // forget windows
    #elif defined _WIN32
    do_windows_specific_thing();
    #endif
#endif

(你不会得到最后一个错误,但是......

$ gcc -E -P -U__linux__ -D__APPLE__ -D_WIN32 test.c
    do_osx_specific_thing();
    do_windows_specific_thing();

……我不认为这就是写它的人的本意。)


许多语言指南告诉你使用#if 0 来“注释掉”你想暂时禁用的大块代码区域。他们这样说是因为块 cmets 不嵌套。如果您尝试使用块注释禁用代码区域,但该区域内有块注释,则注释将提前结束,并且代码可能无法编译。这在 C 没有行 cmets 的日子里更为重要;一些项目仅使用行 cmets 进行注释,保留块 cmets 用于禁用代码。

但是因为 #if 0 ... #endif 中的代码仍然是标记化的,并且嵌套的预处理器条件仍然必须保持平衡,所以您必须小心放置 #if 0#endif 的位置。这通常不是问题,因为在你禁用它之前用于编译的代码,所以它不应该有任何导致标记化错误的东西。

【讨论】:

    【解决方案2】:

    在一般情况下,两者是等价的。

    但是,如果您的“无效 C 或 C++ 代码”包含 cmets,则第一种形式可以工作,而第二种形式则不能。那是因为 C 标准禁止使用叠瓦式 cmets。

     /* Comments /* inside */ comments are not allowed. */
    

    顺便说一句,在这种情况下,#if 0 通常比#ifdef MACRO 更受欢迎。

    #if 0
        Invalid C source code
    #endif
    

    this question

    【讨论】:

    • 注释用于注释,宏用于预处理时间选项。在这样的宏中应该有一个有效的 C 代码。至少,在某些情况下有效。无与伦比的/* 根本不应该存在,#if 不应该存在。
    • 而且,如果你担心/*不匹配,那么你应该担心#endifs中间的“无效C源代码”。
    • 哇,我不得不去看看叠瓦。这不会经常发生:-)
    • MACRO 方式让我可以检查该块是否对我感兴趣。其实我会 __MACRO__ 进入保留的命名空间。
    • @Elazar:#ifdef/#endif 指令可以重叠,但 cmets 不能。我不是指“不匹配的/*,因为它也不适用于匹配的/*
    【解决方案3】:

    是的,它们是等价的,预处理阶段将在编译器正确看到代码之前消除Not valid C or C++ code

    预处理涉及删除 cmets 和 #ifed out 的代码。

    但是如果有人用-DMACRO编译代码,#ifdef版本会给你带来麻烦,最好使用#if 0通过预处理器删除代码。

    【讨论】:

      【解决方案4】:

      标准的相关部分是C11 6.10.1 Conditional inclusion /6

      按顺序检查每个指令的条件。如果它评估为假(零),则该组 它控制的被跳过。

      这意味着,如果各种形式(ififdef 等)中的任何一种评估为 false,则 no 对组的处理已完成,并在以下方面被完全删除后期处理。它确实不会变成评论。

      【讨论】:

        【解决方案5】:

        不,在您的最终代码中,#ifdef 内不会有任何代码痕迹:

        // before
        #ifdef MACRO
            Not valid C or C++ code
        #endif
        // after
        

        预编译后:

        // before
        // after
        

        那里没有剩余代码。

        【讨论】:

        • 评论中也不会有代码,所以我会说是的,它们都是平等的;都是空的
        • 等价但不等价。 OP 错误地认为代码将放在注释部分。真正发生的是,代码被完全剥离。
        • 您甚至不会保留 // before// after cmets,因为 cmets 在预处理阶段之前被剥离。
        • 其实这不是真的。注释也会被预处理器删除。
        • @gx_ 你的意思是在预处理指令阶段之前,即在处理宏和#ifs之前。但是这两个阶段都被认为是预处理器的一部分。
        【解决方案6】:

        如果没有定义 MACRO,它们应该是等价的。注释掉大块代码的典型方式通常是:

        #if 0
        code(); /* possibly with comments. */
        #endif
        

        这允许您禁用大部分代码,即使它们包含 cmets。所以禁用部分代码比普通的 cmets 更好。

        但有一个警告。我遇到了一个编译器,它被这样的东西噎住了:

        #ifdef __GNUC__
        #nonstandardpreprocessordirective
        #endif
        

        其中“nonstandardpreprocessordirective”是仅适用于 GCC 的预处理器指令。我不完全确定标准对此有何规定,但过去它在现实中引起了问题。不过我不记得是哪个编译器了。

        【讨论】:

          【解决方案7】:

          它们很接近,但并不完全。假设 MACRO 未定义(或假设您使用 #if 0 此处其他答案中的建议):

          #ifdef MACRO
          Not valid C or C++ code
          */  - does no harm
          #endif  - oops
          more invalid code
          #endif
          

          和 cmets:

          /*
              Not valid C or C++ code
              #endif - does no harm
              */ - oops
          */
          

          评论用于评论,#ifdef 用于禁用合法代码。任意文本根本不应驻留在源中。

          【讨论】:

            【解决方案8】:

            是的,大多数预处理器(如果不是全部)将同时删除 cmets 和评估为 0 的指令。两者之间的差异主要是功能性的。

            我的建议是使用指令来“注释”代码(#if 0 {} #endif)并使用 cmets 来进行注释(很符合逻辑对吧?)。主要原因是:

            • 只需修改代码中的 1 行即可激活/停用指令。块 cmets 需要在代码的不同行中插入/删除 2 个元素。
            • 指令可以嵌套保留 IF 逻辑,也可以包含块 cmets。 /Block cmets/ 不能嵌套,当您注释可能包含另一个 cmets 的大段代码时,这可能会出现问题。
            #if 0
            ...
            #if 1
            #endif
            ...
            #endif
            
            • 简单的#if 0 指令可以轻松地转换为define 或eval 指令,从而允许更动态的条件代码处理。
            //Classic verbose code line comment
            
            #if 0
            //Directive verbose line or block comment   
            #endif
            
            #define verbose 0
            #if verbose
            //Convenient eval directive to turn on/off this and other verbose blocks
            #endif
            
            • 大多数 IDE 不会突出显示注释块的语法,但会突出显示指令代码的语法。缩进或自动完成等其他功能也是如此。与 #if 0 #endif 块相比,这使得 /**/ 块的可读性很差。此外,使用指令更容易编辑注释代码(例如添加一行或修复某些内容)。

            【讨论】:

              猜你喜欢
              • 2017-01-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-10-20
              • 2016-07-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多