在回复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
是不是完全没用,是不是它会刺激丑陋的代码?这个警告是否有助于编写更好的代码?
答案 0 :(得分:5)
我不认为它有用。相反,有更多的误报而不仅仅是do .. while(0)
成语。想想像
if(sizeof(long) == 8) { /* ... */ }
if(SOME_CONSTANT_MACRO) { /* ... */ }
前者不能被#if
指令替换,后者可以,但是一些编码风格指南更喜欢if
版本,因为仍然对死代码进行语法检查(这不是死的)其他平台或其他编译时配置)有些人发现阅读效果更好。
警告(标准所要求的除外,其中大多数应被视为错误)通常是针对有效但可能不符合预期的代码发出的。 if(0)
或类似的东西看起来很傻,但看起来并不像“语法检查这个否则死代码”之外的东西。它可能会困扰读者,但它是明确的,我不知道这是如何偶然发生的。
从目前给出的例子(我没有让MSVC自行测试),似乎警告是针对C语言意义上的常量表达式(也就是说,不是可以保持不变的东西 - 折叠但在语法上不是常量表达式),因此它不会为if(array)
或if(function)
发出(例如gcc -Wall
会发出警告,因为它可能是一个函数调用)。
while(0,0)
更糟糕的是,在我看来,它会触发gcc -Wall
的警告,指示逗号运算符的左侧没有副作用,我可以想象这个警告偶尔会有用(通常很容易避免)。此警告会随while((void)0,0)
消失。
我建议关闭警告。
答案 1 :(得分:3)
条件表达式是常量的警告肯定可以有用。在许多情况下,它可能会指向代码中的逻辑错误。
例如,如果您写的内容如下:
if (x != NULL) { /* ... */ }
其中x
是一个数组对象,表达式有效,但表达式x
衰减到指向数组初始元素的指针,不能为空指针。我不知道是否会产生相同的错误,但它是同一类例子。
当然总是有用。在你的情况下,
do { /* ... */ } while (0)
idiom是编写宏定义的最佳方式,该宏定义旨在用于需要语句的上下文中。在另一个上下文中使用while (0)
可能是一个逻辑错误[*]。
不幸的是,你的编译器并不认为它是一种常见的习语。产生良好的警告是棘手的;编译器必须超越语言规则并推断程序员的意图。
在这种情况下,使用某些特定于编译器的方法来抑制警告(只要它不会破坏其他编译器的代码)可能是最好的方法。在所有情况下使用命令行选项来抑制警告将是过度的;您可能会错过代码中其他地方的有效警告。
显然写while (0,0)
而不是while (0)
可以避免警告。如果您这样做,您应该添加一个注释,清楚地表明它是您的特定编译器的解决方法。编译器不应该警告while (0,0)
或任何其他等效代码。
[*]如果您希望能够使用do { /* ... */ } while (0)
跳出来,那么编写break
语句是有意义的。