为什么要使用 MACRO + 0! = 0

在我当前的代码库中,我看到了以下模式:

#if SOMETHING_SUPPORTED+0 != 0
...
#endif

不幸的是,这是一个非常古老的代码库,没有人知道它是如何以及为什么开始的。我认为它是从 C 语言开始的,然后用类慢慢地转换成 C 语言,现在趋向于 C + +

我看不出使用前面的结构而不是“经典”结构有什么明显的优势,但也许我遗漏了什么:

#if SOMETHING_SUPPORTED
...
#endif

你知道为什么要用 #if MACRO+0 != 0而不是 #if MACRO吗?

4909 次浏览

让我们做一张桌子!

X       #if X     #if X+0 != 0
<undef> false     false
<empty> error     false
0       false     false
1       true      true
2       true      true
a       false     false
xyz     false     false
12a     error     error
12 a    error     error

因此,我们发现的唯一区别(感谢评论者)是定义了 X 但没有值(如空字符串)的情况。我以前从没见过 +0 != 0变体。

这是另一种写作方式

 #if defined(MACRO) && MACRO != 0

+ 0是为了确保结果是一个数字。如果没有定义 MACRO,则可以避免由 #if MACRO != 0引起的语法错误。

质量很差,不到万不得已不要乱动。

X被定义为空的情况下,#if X+0 != 0#if X不同(注意: 这与没有定义 X的情况不同) ,例如:

#define X


#if X          // error
#if X+0 != 0   // no error; test fails

定义空宏是非常常见的: 项目配置可能会生成一些公共头文件,其中包含一系列行 #define USE_FOO#define USE_BAR,以支持系统支持的特性,等等。

!= 0是多余的,代码可能只是 #if X+0


因此,使用 #if X+0的好处是,如果将 X定义为空,那么编译将在跳过代码块的情况下继续进行,而不会触发错误。

这是否是一个好主意是有争议的,个人而言,我会使用 #ifdef的布尔宏,如 USE_SOME_FEATURE,和 #if的宏,其中的值可能是一个范围的整数,例如,我想看到一个错误,如果我不小心使用 #if的东西定义为空。

这里的线索是代码库非常古老。

这个技巧之所以存在,可能是因为代码曾经被移植到一个带有一些非常老的预处理器的编译器上,这些预处理器在预处理器 #if条件中不将 未定义宏视为0。

也就是说,从1989年开始,ANSI C 被标准化了,如果我们有:

# if foo + bar-xyzzy

该指令受宏替换的影响,因此,如果 foobarxyzzy是宏,它们将被替换。然后,任何未被替换的剩余标识符将被替换为 0。因此,如果 foo被定义为 42,而 barxyzzy根本没有被定义,我们得到:

#if 42 + 0 - 0

而不是,比如说,糟糕的语法:

#if 42 + -

或者其他一些行为,比如诊断 bar没有被定义。

在未定义的宏被当作空格处理的预处理器上,#if SOMETHING_SUPPORTED扩展到仅仅是 #if,这就是错误的。

这是这个 IDENT+0技巧真正有意义的唯一方法。如果可以依靠符合 ISO C 标准的预处理,那么您根本不会想要这样做。

原因是,如果 SOMETHING_SUPPORTED应该有数值,那么将它定义为一个简单的空白就是错误的分散思维。理想情况下,您应该检测这种情况何时发生,并通过诊断来停止编译。

其次,如果您的 支持这样一种分散的用法,那么您几乎肯定希望显式定义但是空白的符号表现得好像它的值是1,而不是0。否则,你就是在设陷阱。有人可能会在编译器命令行上这样做:

 -DSOMETHING_SUPPORTED=$SHELL_VAR  # oops, SHELL_VAR expanded to nothing

或代码:

 #define SOMETHING_SUPPORTED  /* oops, forgot "1" */

没有人会去 一个 #define-D为一个符号的意图转向 关掉的功能,它控制!插入没有 1#define SOMETHING_SUPPORTED的程序员将会对

 #if SOMETHING_SUPPORTED+0

它跳过了原本要启用的材料。

这就是为什么我怀疑很少有 C 程序员读到过这样的用法,也是为什么我怀疑这只是一个预处理器行为的解决方案,其预期效果是在缺少 SOMETHING_SUPPORTED时跳过块。事实上,它设置了一个“程序员陷阱”只是变通方法的副作用。

要解决这样一个预处理器问题而不造成程序员陷阱,就要在翻译单元的早期某个地方具备以下特性:

#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif

其他地方只用 #if SOMETHING_SUPPORTED。也许最初的程序员没有想到这种方法,也许那个程序员认为 +0的技巧很巧妙,并且赋予了它自我约束的价值。