#ifdef MACRO是否等同于评论

时间:2013-06-14 08:47:53

标签: c comments c-preprocessor conditional-compilation

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

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

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

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

8 个答案:

答案 0 :(得分:6)

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

但是,如果您的“无效的C或C ++代码”包含注释,则第一个表单将起作用,而第二个表单不起作用。这是因为C标准禁止使用叠加的评论。

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

BTW,#if 0在这种情况下通常优先于#ifdef MACRO

#if 0
    Invalid C source code
#endif

请参阅this question

答案 1 :(得分:6)

这取决于“无效的C或C ++代码”的含义。

评论中的文字不必符合该语言的大部分规则。它甚至没有被标记化。这完全有效:

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

它必须遵守的唯一规则是控制评论结束的那些规则。人们经常会在行注释中反斜杠换行(注意SO的语法荧光笔会出错!)

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

并且不经常(如果只是因为每个C教程都警告你这一点)通过块注释而不是嵌套(语法高亮显示得到这个):

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

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

  

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

有两个重要的部分。首先,文本标记化的,因此 必须是预处理标记的有效序列。

#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“注释掉”要暂时禁用的大型代码区域。他们这样说是因为块注释没有嵌套。如果您尝试使用块注释禁用代码区域,但该区域内存在块注释,则注释将过早结束,并且可能代码将无法编译。在C没有行评论的日子里,这一点更为重要;一些项目仅使用行注释进行注释,为禁用代码保留块注释。

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

答案 2 :(得分:5)

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

预处理涉及删除评论以及#if编译的代码。

但如果有人使用-DMACRO编译代码,则#ifdef版本会让您遇到麻烦,最好使用#if 0通过预处理器删除代码。

答案 3 :(得分:1)

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

  

按顺序检查每个指令的条件。如果它的计算结果为false(零),则为该组   它控制的内容被跳过。

这意味着,如果各种表单中的任何一种(ififdef等)评估为false,则 no 处理该组已完成且完全是在后续处理阶段被删除。 会变成评论。

答案 4 :(得分:0)

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

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

预先编译后:

// before
// after

其中有 no 剩余代码。

答案 5 :(得分:0)

如果未定义MACRO,则它们应该是等效的。注释掉大块代码的典型方法通常是:

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

这允许您禁用大部分代码,即使它们包含注释。因此,禁用部分代码比正常注释更好。

但有一点需要注意。我遇到了一个像这样的东西窒息的编译器:

#ifdef __GNUC__
#nonstandardpreprocessordirective
#endif

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

答案 6 :(得分:0)

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

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

和评论:

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

评论用于评论,#ifdef用于停用 legal 代码。任意文本都不应该存在于源文件中。

答案 7 :(得分:0)

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

我的建议是使用指令"评论"代码(#if 0 {} #endif)并仅使用注释进行评论(非常逻辑对吗?)。主要原因是:

  • 只需修改代码中的1行即可激活/取消激活指令。阻止注释需要在代码的不同行中插入/删除2个元素。
  • 指令可以嵌套,保留IF逻辑,也可以包含块注释。 / 阻止评论 /无法嵌套,当您评论可能包含其他评论的大部分代码时,这可能会出现问题。
#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都没有突出显示注释块的语法,但是DO突出显示了指令代码的语法。与缩进或自动完成等其他功能相同。与#if 0 #endif块相比,这使得/ ** /块的可读性相当差。使用指令也可以更容易地编辑注释代码(例如添加一行或修改某些内容)。