【问题标题】:is `warning C4127` (conditional expression is constant) ever helpful?`warning C4127`(条件表达式是常量)有用吗?
【发布时间】:2015-05-13 04:15:34
【问题描述】:

在回复this 帖子时,我建议使用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)

也被一些程序员称为“猫头鹰”,因为(0,0) 看起来像猫头鹰。

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

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

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

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

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

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

【问题讨论】:

  • @Bathsheba: 但for(;;)do{...}while(0) 的含义不同!!!!
  • @GiulioFranco:它没有解决do { ... } while(0) 解决的问题。 if(foo) for(;;) { ...; break; }; else bar(); 是语法错误。
  • @BoykoPerfanov #define ABC(X) {something;} 如果调用为:if(cond) ABC(42); else {},编译将失败
  • 不,我想这永远不会有帮助。顺便说一句,猫头鹰很可爱。

标签: c++ c visual-c++ c4127


【解决方案1】:

我不认为它有用。相反,除了do .. while(0) 成语之外,还有更多的误报。想想像

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

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

警告(标准要求的警告除外,其中大部分应被视为错误)通常针对有效但可能执行超出预期的代码的代码发出。 if(0) 或类似的东西看起来很傻,但看起来好像不是“语法检查这个否则死的代码”之外的东西。这可能会让读者感到困惑,但它是明确的,我不明白这怎么会意外发生。

从到目前为止给出的示例(我还没有让 MSVC 自己测试),似乎警告是针对 C 语言意义上的常量表达式(也就是说,不是可以是常量的东西- folded 但在语法上不是一个常量表达式),因此它不会针对if(array)if(function) 发出(例如gcc -Wall 会发出警告,因为它可能是一个函数调用)。

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

我建议关闭警告。

【讨论】:

    【解决方案2】:

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

    例如,如果您编写如下内容:

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

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

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

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

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

    很遗憾,您的编译器没有将其识别为常见习语。生成好的警告很棘手;编译器必须超越语言规则,推断程序员的意图。

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

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

    [*] 如果您希望能够使用break 跳出它,编写do { /* ... */ } while (0) 语句是有意义的。

    【讨论】:

    • 从到目前为止给出的示例中,看起来警告仅针对常量表达式触发(不适用于可以常量折叠的非常量构造),因此不会针对 @987654331 发出警告@。不然为什么if(0,0)不触发?
    猜你喜欢
    • 2015-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 2016-03-01
    相关资源
    最近更新 更多