8

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

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

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

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

4

8 回答 8

11

这取决于您所说的“无效的 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)的确切内容是

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

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

#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

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

$ 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. 这通常不是问题,因为在您禁用它之前用于编译的代码,所以它不应该有任何东西导致标记化错误。

于 2017-02-10T13:57:35.073 回答
7

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

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

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

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

#if 0
    Invalid C source code
#endif

看到这个问题

于 2013-06-14T08:50:25.620 回答
5

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

预处理涉及删除注释和删除的代码#if

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

于 2013-06-14T08:50:44.217 回答
1

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

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

这意味着,如果各种形式( 等)中的任何一种if评估ifdef为假,则不会对该组进行任何处理,并且在处理的后期阶段将其完全删除。它不会变成评论。

于 2013-06-14T08:55:47.500 回答
0

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

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

预编译后:

// before
// after

那里没有剩余的代码。

于 2013-06-14T08:52:56.603 回答
0

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

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

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

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

#ifdef __GNUC__
#nonstandardpreprocessordirective
#endif

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

于 2013-06-14T09:02:25.657 回答
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用于禁用法律代码。任意文本根本不应该存在于源中。

于 2013-06-14T09:04:49.060 回答
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 不会突出显示注释块的语法,但会突出显示指令代码的语法。缩进或自动完成等其他功能也是如此。与 #if 0 #endif 块相比,这使得 /**/ 块的可读性很差。此外,使用指令更容易编辑注释代码(例如添加一行或修复某些内容)。
于 2015-09-21T09:33:58.087 回答