【问题标题】:Why would one use MACRO+0 !=0为什么要使用 MACRO+0 !=0
【发布时间】:2023-04-09 05:13:01
【问题描述】:

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

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

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

我看不出使用以前的构造代替“经典”有任何明显的优势,但也许我遗漏了一些东西:

#if SOMETHING_SUPPORTED
...
#endif

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

【问题讨论】:

标签: c++ c


【解决方案1】:

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

这个技巧可能存在,因为代码曾经被移植到带有一些非常旧的预处理器的编译器,该预处理器不会在预处理器#if 条件中将 undefined 宏视为 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 符号以关闭它控制的功能!插入#define SOMETHING_SUPPORTED 而没有1 的程序员会对

 #if SOMETHING_SUPPORTED+0

它会跳过打算启用的材料。

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

要在不造成程序员陷阱的情况下解决这样的预处理器问题,需要在翻译单元的早期某处拥有:

#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif

然后在其他地方使用#if SOMETHING_SUPPORTED。可能最初的程序员没有想到这种方法,或者可能那个程序员认为+0 的技巧很巧妙,并重视它的自包含。

【讨论】:

  • 你没有错,但是......我所知道的所有 Unixy 编译器都将 -DTHING 视为 -DTHING=1 的简写;如果你真的希望宏扩展为空,你必须说-DTHING=
  • @zwol 可能通过-DFOO=$FOO_ON 发生,其中shell var 扩展为空,而不是预期的0 或1! (已编辑)。
  • @zwol 确实。这可能是因为构建脚本是为单个用例量身定制的,始终在几乎相同的输入上运行,因此它们的逻辑面向该用例的预期行为。 (与人们为自己使用而编写的一次性脚本不同,甚至可能更糟)。
【解决方案2】:

#if X+0 != 0X被定义为空的情况下与#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 定义为空,我会想看到一个错误。

【讨论】:

  • -DNDEBUG 这样的标志会将NDEBUG 定义为1,而不是空的。
  • @DietrichEpp 你是对的。 -DNEBUG= 会将其定义为空
  • 我从未见过这样的错误。根据我的经验,#if 的评估结果为 false(这可能仍然是不希望的)。编辑:我的立场得到纠正,这确实会产生错误。不知道我过去看到了什么。也许它是特定于编译器的。
【解决方案3】:

让我们做一张桌子!

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

【讨论】:

  • @M.M:我也是。我只是测试一下。它按预期工作。所以我把我的答案误删了。
  • @DietrichEpp 这个问题是关于 X 未定义的,而不是 X 空的。我猜这个答案中的表格应该将“empty”重命名为“undefined”,并为“empty”添加一个新行
  • @LokiAstari 也许你做错了什么,see here
  • 如果滥用运算符优先级规则,for example#define X 1^2 会发现其他不匹配的地方。
【解决方案4】:

这是另一种写作方式

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

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

质量差的东西,但除非万不得已,否则不要乱搞。

【讨论】:

  • #if defined(MACRO) && MACRO != 0 => 未定义宏时为假 #if MACRO+0 !=0 => 未定义宏时为假,但原因不同: 0+0 != 0 结果相同,但不等价stackoverflow.com/questions/5085392/…
  • @Felics 我无法将任何有用的含义归因于“它们具有相同的结果但它们不等效”。我并没有说它们实际上等价的。你的观点仍然模糊不清。
  • 我想指出#if MACRO+0 !=0#if defined(MACRO) &amp;&amp; MACRO != 0 的写法不同。例如:#define MACRO 会导致 #if defined(MACRO) &amp;&amp; MACRO != 0 的构建错误,并且可以正常编译 #if MACRO+0 !=0
  • 这是不正确的:#define MACRO 之后,OP 代码编译但你的版本没有
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 2018-05-01
  • 2011-05-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多