`warning C4127`(条件表达式是常量)有帮助吗?

is `warning C4127` (conditional expression is constant) ever helpful?

在回答 this post 时,我建议对多行宏使用 do {...} while(0)

在 MSVC 上,我发现这段代码抛出:

warning C4127: conditional expression is constant

为了使代码没有警告,我需要选择这些丑陋的替代方案之一:

选项 1

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif
code_using_macro_that_generates_C4217;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

选项 2
将我的宏定义为:

#define MULTI_LINE_MACRO do { ... } while(0,0)

#define MULTI_LINE_MACRO do { ... } while((void)0,0)

也被一些程序员称为 "owl",因为 (0,0) 看起来像 owl。

选项 3
定义一个不生成警告的新宏 WHILE_0 并使用它代替 while(0)

问题
我相信所有的选择都或多或少是可怕的。为什么 MSVC 会为看似正确的代码生成此警告并促使我向我的代码添加一些丑陋的内容以保持代码警告免费?

我相信条件中的常量表达式是完全有效和有用的,特别是在基于编译器优化代码能力的构造中。

此外,对于这样的代码,我没有得到 warning C4127

void foo(unsigned bar)
{
    while (bar >= 0)
        ;
} 

我的问题是:warning C4127: conditional expression is constant 不是完全没用吗,它不会激发丑陋的代码吗?此警告是否有助于编写更好的代码?

条件表达式为常量的警告肯定有用。在许多情况下,它可能指向代码中的逻辑错误。

例如,如果您这样写:

if (x != NULL) { /* ... */ }

其中x是数组对象,表达式有效,但表达式x衰减为指向数组初始元素的指针,不能 是一个空指针。我不知道这是否会产生相同的错误,但这是同类事件的一个例子。

当然,它并不总是有用。在你的情况下,

do { /* ... */ } while (0)

习语是编写宏定义的最佳方式,该宏定义旨在用于需要语句的上下文中。在另一个上下文中使用 while (0) 很可能是逻辑错误 [*].

很遗憾,您的编译器无法将其识别为常用习惯用法。产生良好的警告很棘手;编译器必须超越语言规则并推断程序员的意图。

在这种情况下,使用一些特定于编译器的方法来抑制警告(只要它不破坏其他编译器的代码)可能是最好的方法。在所有情况下使用命令行选项来抑制警告都太过分了;您可能会错过代码中其他地方的有效警告。

显然写 while (0,0) 而不是 while (0) 可以避免警告。如果这样做,您应该添加一条注释,清楚地表明它是针对您的特定编译器的解决方法。编译器不应该警告 while (0,0) 或任何其他等效代码没有特别的原因。

[*]如果想能用break跳出来的话,写个do { /* ... */ } while (0)语句才有意义

我不认为它有用。相反,误报比仅 do .. while(0) 成语更多。想想像

这样的结构
if(sizeof(long) == 8) { /* ... */ }
if(SOME_CONSTANT_MACRO) { /* ... */ }

前者不能被 #if 指令取代,后者可以,但一些编码风格指南更喜欢 if 版本,因为语法检查仍然对死代码进行(这不是在其他平台上或与其他编译时配置一起死了),有些人觉得它更好读。

警告(标准要求的警告除外,其中大部分应被视为错误)通常针对有效代码发出警告,但可能会执行与预期不同的操作。 if(0) 或类似的东西看起来很傻,但看起来不像 "syntax check this otherwise dead code" 以外的东西。这可能会让 reader 感到困惑,但它是明确的,我不明白这怎么会意外发生。

从目前给出的示例来看(我还没有自己测试的 MSVC),警告似乎是针对 C 语言意义上的常量表达式(也就是说,不是可以常量折叠但在句法上不是常量表达式),因此它不会为 if(array)if(function) 发出(例如 gcc -Wall 确实发出警告,因为它可能旨在成为函数调用)。

while(0,0) 更糟糕,在我看来,它会触发 gcc -Wall 逗号运算符左侧的警告而没有副作用,我可以想象偶尔会出现警告有用(而且通常很容易避免)。此警告随 while((void)0,0).

消失

我建议关闭警告。