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


72

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

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

不幸的是,这是一个非常古老的代码库,没人知道它是如何开始的。我认为它始于C,后来随着类逐渐转换为C,现在趋向于C ++

我看不到使用以前的构造而不是“经典”的任何明显优势,但是也许我缺少了一些东西:

#if SOMETHING_SUPPORTED
...
#endif

你知道为什么要用一个#if MACRO+0 != 0代替#if MACRO吗?


4
可能相关: 测试空宏定义
t.niese

3
!= 0肯定是多余的
MM

Answers:


70

这里的线索是代码库很旧。

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

也就是说,从1989年ANSI C开始,我们标准化了以下条件:

#if foo + bar-xyzzy

该指令是受宏替换,这样,如果foobar或者xyzzy是宏,它们被替换。然后,将所有未替换的剩余标识符替换为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与意图转向的一个符号关闭它所控制的功能!#define SOMETHING_SUPPORTED没有插入的程序员1将对的行为感到惊讶

 #if SOMETHING_SUPPORTED+0

跳过原本打算启用的材料。

这就是为什么我怀疑很少有C程序员阅读过此用法,以及为什么我怀疑这只是预处理程序行为的一种解决方法,其预期效果是在SOMETHING_SUPPORTED缺少该代码时跳过该块。它造成“程序员陷阱”的事实只是解决方法的副作用。

要在不创建程序员陷阱的情况下解决此类预处理程序问题,请在翻译单元的早期某个位置进行以下操作:

#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif

然后在其他地方使用#if SOMETHING_SUPPORTED。最初的程序员可能没有采用这种方法,或者程序员认为这种+0技巧很巧妙,并在其独立性上重视了价值。


1
你没看错,但是……我所知道的所有Unixy编译器都可以看作-DTHING是速记-DTHING=1;如果您实际上希望宏扩展为无内容,则不必说-DTHING=
zwol

1
@zwol可能通过发生 -DFOO=$FOO_ON,其中shell var扩展为零,而不是预期的0或1!(已编辑)。
哈兹

@zwol的确如此。这可能是因为构建脚本是针对单个用例量身定制的,并且始终在几乎相同的输入上运行,因此它们的逻辑适用于该用例的预期行为。(与人们为自己使用编写的一次性脚本不同,甚至更糟)。
哈兹

44

#if X+0 != 0是不同#if X的,其中的情况下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定义为空的东西,我会希望看到一个错误。


1
像这样的标志-DNDEBUG将定义NDEBUG1,不能为空。
Dietrich Epp

@ DietrichEpp你是对的。-DNEBUG=将其定义为空
MM

我从未见过这样的错误。根据#if我的经验,正义者认为是假的(可能仍然是不希望的)。编辑:我站在纠正,这确实产生一个错误。不知道我过去看过什么。也许是特定于编译器的。
卡梅伦

35

摆一张桌子吧!

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变体。


1
@MM:我也是。我只是测试一下。它按预期工作。因此,我删除了错误的答案。
马丁·约克


@DietrichEpp这个问题是关于X未定义的,不是X空的。我想此答案中的表格应将“空”重命名为“未定义”,并为“空”添加新行
MM

@LokiAstari也许你做错了什么,在这里看到的
MM

如果您滥用运算符优先级规则,则可以找到其他不匹配项,例如使用#define X 1^2
Ilmari Karonen

7

这是另一种写作方式

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

那里+0以确保结果是数字。如果MACRO未定义,则可以避免引起的语法错误#if MACRO != 0

质量低劣的东西,但是除非您必须,否则不要将其弄乱。


#if defined(MACRO)&& MACRO!= 0 =>如果未定义宏则为false #if MACRO + 0!= 0 =>如果未定义宏则为false,但由于不同的原因:0 + 0!= 0相同的结果,但他们并不等同 stackoverflow.com/questions/5085392/...
米尔恰伊斯帕斯

@Felics我无法将任何有用的含义归因于“它们具有相同的结果,但它们并不等效”。我没有说过它们实际上等效的。您的观点仍然不清楚。
user207421'5

我想指出,这#if MACRO+0 !=0不是另一种写作方式#if defined(MACRO) && MACRO != 0。例如:#define MACRO将导致的构建错误#if defined(MACRO) && MACRO != 0并且会编译良好 #if MACRO+0 !=0
Mircea Ispas

8
这是不正确的:之后 #define MACRO,OP代码会编译,但您的版本却无法编译
MM
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.