在 C 宏中,是不是应该更喜欢 do ... while(0,0) 而不是 do ... while(0)?

Posted

技术标签:

【中文标题】在 C 宏中,是不是应该更喜欢 do ... while(0,0) 而不是 do ... while(0)?【英文标题】:In C macros, should one prefer do ... while(0,0) over do ... while(0)?在 C 宏中,是否应该更喜欢 do ... while(0,0) 而不是 do ... while(0)? 【发布时间】:2009-12-05 22:44:22 【问题描述】:

一位客户最近对我雇主的 C 代码库进行了静态分析,并向我们提供了结果。有用的补丁包括将著名的do ... while(0) 宏更改为do ... while(0,0) 的请求。我了解他们的补丁在做什么(使用序列运算符 return 评估第二个“0”的值,所以效果是一样的)但不清楚他们为什么偏爱第二个形式超过第一种形式。

是否有正当理由选择第二种形式的宏,还是我们客户的静态分析过于迂腐?

【问题讨论】:

您客户的静态分析工具有些过分了,这是肯定的。 我会选择“过于多余”。 我想不出任何理由这样做。也许某些工具会抱怨“这永远不会发生”的 while(0) 条件,但任何此类工具都应该 1) 认识到这是 C 中带有宏的非常常见的习语,以及 2) 区分 do...while (0) 和同时 (0)。你问过他们理由吗? 是的,(0,0) 看起来像一只猫头鹰。也许他们认为你的代码需要更多的猫头鹰。 在 C 和 C++ 中,0,0 不是常量表达式,但 0 是。因此,编译器更有可能警告后者而不是前者。但是编译器也更有可能优化后者而不是前者 - 所以我不太明白他们在改变它时会赢得什么。 【参考方案1】:

只是猜测他们为什么会建议使用

do  ...  while(0,0)

结束

do  ...  while(0)

即使两者之间没有行为差异,也应该没有运行时成本差异。

我的猜测是静态分析工具抱怨while 循环在更简单的情况下由一个常量控制,而在使用0,0 时则不会。客户的建议可能只是为了让他们不会从该工具中得到一堆误报。

例如,我偶尔会遇到一些情况,我希望有一个由常量控制的条件语句,但编译器会抱怨条件表达式计算为常量的警告。然后我必须跳过一些障碍来让编译器停止抱怨(因为我不喜欢虚假的警告)。

您客户的建议是我用来消除该警告的方法之一,尽管在我的情况下它不是控制while 循环,而是处理“总是失败”的断言。有时,我会有一个永远不应该执行的代码区域(可能是 switch 的默认情况)。在那种情况下,我可能有一个断言总是失败并显示一些消息:

assert( !"We should have never gotten here, dammit...");

但是,我使用的至少一个编译器会发出一个关于表达式总是评估为假的警告。但是,如果我将其更改为:

assert( ("We should have never gotten here, dammit...", 0));

警告消失了,大家都很开心。我猜你客户的静态分析工具也是。请注意,我通常会隐藏在宏后面的那一点跳跃,例如:

#define ASSERT_FAIL( x) assert( ((x), 0))

能够告诉工具供应商解决问题可能会很好,但在某些合理的情况下,他们可能确实想要诊断由常量布尔表达式控制的循环。更不用说即使你说服了工具供应商做出这样的改变,这对你下一年左右的时间也没有帮助,可能需要真正得到修复。

【讨论】:

+1 可能是这样,但如果是这种情况,则应使用命令行选项禁用这些警告。当您可以关闭不必要的箍时,为什么要跳过箍? 嗯,首先,使用编译器或工具选项可能会被认为是一种跳跃形式。使用 makefile 或任何控制你的构建的东西通常比 C 预处理器hackery 更黑魔法(这说明了很多)。另一方面,您可能不希望对所有内容都禁用警告 - 可能只是针对似乎需要这些愚蠢技巧的调试宏。 现在我的静态分析抱怨,“多余的左参数到逗号。”有时你就是赢不了。【参考方案2】:

使用 while(0,0) 可防止 Microsoft 编译器生成有关常量条件的警告(警告 C4127)。

启用此警告后(例如,使用 /W4 或 /Wall),可以通过这个漂亮的小技巧 (see this other thread) 根据具体情况将其关闭。

编辑:由于 Visual Studio 2017 15.3,while(0) 不再发出警告(参见Constant Conditionals)。你可以摆脱你的(0,0)

【讨论】:

出于某种原因,我对此并不感到惊讶。【参考方案3】:

好吧,我会去找答案的:

是否有正当理由选择第二种形式的宏...?

没有。没有正当理由。两者总是评估为假,任何体面的编译器都可能将第二个编译器变成程序集中的第一个。如果有任何理由使它在某些情况下无效,那么 C 的存在时间已经足够长,足以被比 I 更强大的大师发现。

如果你喜欢你的代码让猫头鹰盯着你看,请使用while(0,0)。否则,请使用其他 C 编程世界使用的东西,并告诉您客户的静态分析工具来推动它。

【讨论】:

它不会评估任何东西; do-loop 不是表达式,也没有任何值。尝试分配它 int x = macro() ;,编译失败。 我指的是00,0的评估,而不是一般的do while(0)循环。 “什么好处?”:在 while 循环条件中使用 0,0 所传达的好处。静态分析似乎相信有一个,但这个答案虽然被接受并没有揭示这一点,所以我们不知道。 @Chris:我明白您的点,但出于好奇,我希望有人能启发我们了解为什么该工具会建议这样做 - 即使想法是有缺陷的。我在任何地方都找不到这样的成语。即使是 MISRA C 指南也不建议这样做。如果 OP 告诉我们该工具可能是什么,那会有所帮助吗? 0,0 使 VC++ 警告 4127 消失。这就是这种模式存在的正当理由。如果您认为 0,0 是不可接受的 hack,那么为什么您认为使用 dowhile(0) 本身并不是更糟糕的 hack?

以上是关于在 C 宏中,是不是应该更喜欢 do ... while(0,0) 而不是 do ... while(0)?的主要内容,如果未能解决你的问题,请参考以下文章

如果比较列表,Clojure的deftest宏中的断言错误

为啥我应该更喜欢单个'await Task.WhenAll'而不是多个等待?

我应该总是更喜欢 EXISTS 而不是 SQL 中的 COUNT() > 0 吗?

MS Visual Studio 2010 C++ 预处理器 - 如果函数在宏中定义并在其他地方调用,未定义时是不是有任何开销

有没有办法删除 C 宏中的引号?

为啥我应该更喜欢“显式类型的初始化程序”习语而不是显式给出类型