#if 0…#endif块到底是做什么的?


124

C / C ++中

#if 0/ #endif块之间放置的代码会怎样?

#if 0

//Code goes here

#endif

代码是否只是被跳过而没有被执行?


17
这是一种用于注释掉大量代码或允许测试是否包含代码块的技术。如果没有此功能,则必须在每行前面加上前缀,//或者在开头部分,/*然后在结尾部分*/。后一种技术的问题在于注释不会嵌套,因此开发人员必须检查和处理*/开始和结束之间的任何内容。
汤玛斯·马修斯

Answers:


141

它不仅不被执行,甚至不被编译。

#if是一个预处理程序命令,在实际编译步骤之前会对其进行评估。该块内的代码不会出现在已编译的二进制文件中。

它通常用于临时删除代码段,以便稍后将其重新打开。


1
任何形式的评论都是如此。重要的区别是嵌套。
Samy Bencherif

73

除了注释之间的一个重要区别外,这与注释掉该块相同:嵌套不是问题。考虑以下代码:

foo();
bar(x, y); /* x must not be NULL */
baz();

如果要注释掉,可以尝试:

/*
foo();
bar(x, y); /* x must not be NULL */
baz();
*/

zz 语法错误!为什么?由于块注释不嵌套,所以(你可以从SO的语法高亮看到)的*/单词“NULL”结束后的评论,使得baz没有被注释掉的呼叫,并在*/baz一个语法错误。另一方面:

#if 0
foo();
bar(x, y); /* x must not be NULL */
baz();
#endif

注释掉整个事情。并且#if 0s将彼此嵌套,如下所示:

#if 0
pre_foo();
#if 0
foo();
bar(x, y); /* x must not be NULL */
baz();
#endif
quux();
#endif

当然,如果不正确地进行注释,这可能会造成一些混乱,并给维护带来麻烦。


5
请注意,#if中的代码必须在词法上正确(与注释相反),并且预处理程序指令仍然有效(同上)。
jpalecek 2010年

@David:从词法上讲它不是正确的,但仍会编译。因此,代码不必在词法上正确。
丹尼斯·齐克福斯

1
@Dennis,我foo.c:3: unterminated string or character constant来自gcc,您在使用什么?
David X

18

它会永久注释掉该代码,因此编译器将永远不会对其进行编译。

如果愿意,编码器以后可以更改#ifdef以使该代码在程序中进行编译。

就像代码不存在一样。


15

#if 0…#endif块到底是做什么的?

它告诉您,作者显然从未听说过版本控制系统。反过来,这告诉您要跑得尽可能远……


12

我想补充一下这种#else情况:

#if 0
   /* Code here will NOT be complied. */
#else
   /* Code will be compiled. */
#endif


#if 1
   /* Code will be complied. */
#else
   /* Code will NOT be compiled. */
#endif

7

当预处理器看到#if时,它将检查下一个令牌是否具有非零值。如果是这样,它将为编译器保留代码。如果没有,它将删除该代码,因此编译器将永远不会看到它。

如果有人说#if 0,那么他们正在有效地注释掉代码,因此它将永远不会被编译。您可以认为这就像他们将/ * ... * /放在它周围一样。它并不完全相同,但是效果相同。

如果您想详细了解发生了什么,可以经常看一下。在预处理程序运行之后,许多编译器将允许您查看文件。例如,在Visual C ++上,switch / P命令将执行预处理器并将结果放入.i文件中。


不完全的。预处理程序按行而不是按令牌解析。根据您的解释,不可能说出例如,#if WIN32 || __CYGWIN__但这确实可以预期。
Ben Voigt 2010年

我正在简化。如果有或,它将检查任一令牌是否为非零。同样,如果存在,则将检查两者是否均为非零。
史蒂夫·罗

3

以a开头的行#预处理程序指令#if 0 [...] #endif块不会传递给编译器,并且不会生成任何机器代码。

您可以通过源文件演示预处理器的情况ifdef.cxx

#if 0
This code will not be compiled
#else
int i = 0;
#endif

运行gcc -E ifdef.cxx将向您显示编译的内容。

您可以选择使用这种机制来防止在开发周期中编译代码块,但是您可能不希望将其检入到源代码管理中,因为这只会增加代码的混乱性并降低可读性。如果这是一段已被注释掉的历史代码,则应将其删除:源代码控件包含历史记录,对吗?

同样,对于CC ++,答案可能是相同的,但是没有称为C / C ++的语言,并且不是引用这种语言的好习惯。


4
用C / C ++作为“ C或C ++”的简写有什么问题?您是否诚实地认为任何人都会被认为存在一种叫做“ C / C ++”的语言所迷惑?
Christopher Barber'5

1
@Chris:人们经常问“如何在C / C ++中执行X?”之类的问题。这是一个荒谬的问题;您正在使用一种或另一种进行编码,并且如果您尚未选择,则应在这一事实中保持明确。因此,是的,人们对于存在一种称为“ C / C ++”的语言感到困惑
Dennis Zickefoose 2010年

1
多么荒谬的声明!成千上万的问题的答案与C和C ++有关。请记住,这是一个提出问题的网站。仅因为您在问一个一般性问题,并不意味着您不知道使用哪种语言。如果您怀疑是这种情况(如本问题所述),那么指定C或C ++有什么帮助?如果对适用于C或C ++的每个问题都询问两次,是否会有所帮助?
Christopher Barber 2010年

@Christopher:没错,有很多问题的答案与C,C ++和Objective-C一样重要(这就是其中之一)。堆栈溢出有一个方便的标记系统来指示问题所属的语言。正如@Dennis所提到的,SO(和其他编程论坛[论坛?])有太多的人对C语言与其他C语言派生的语言之间的界限感到困惑,他们将这些语言互换地称为C / C ++,使之成为现实。很难用适当的语言回答问题。
Johnsyweb

2

不完全的

int main(void)
{
   #if 0
     the apostrophe ' causes a warning
   #endif
   return 0;
}

它在gcc 4.2.4中显示“ tc:4:19:警告:缺少终止'字符”


2
该警告是由预处理器而不是编译器生成的。编译器只会看到:#1“ tc”#1“ <内置>”#1“ <命令行>”#1“ tc” int main(void){return 0; }
Johnsyweb

0

这是一种廉价的注释方法,但我怀疑它可能具有调试潜力。例如,假设您有一个将值输出到文件的构建。您可能不希望在最终版本中使用它,因此可以使用#if 0 ... #endif。

另外,我怀疑出于调试目的而做的更好方法是:

#ifdef DEBUG
// output to file
#endif

您可以执行类似的操作,这可能更有意义,您只需定义DEBUG即可查看结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.