44

由于这个答案中描述的原因,我经常在我的#defines 中使用 do-while(0) 构造。此外,我正在尝试使用编译器的尽可能高的警告级别来捕捉更多潜在问题,并使我的代码更加健壮和跨平台。所以我通常使用-Wallgcc 和/WallMSVC。

不幸的是,MSVC 抱怨 do-while(0) 构造:

foo.c(36) : warning C4127: conditional expression is constant

我应该怎么处理这个警告?

只是为所有文件全局禁用它?这对我来说似乎不是一个好主意。

4

21 回答 21

46

摘要:在这种特殊情况下,此警告 (C4127) 是一个微妙的编译器错误。随意禁用它。

深入:

它旨在捕捉逻辑表达式在非显而易见的情况下评估为常量的情况(例如if(a==a && a!=a),,并且不知何故,它使while(true)其他有用的构造变为无效。

如果您想打开此警告, Microsoft 建议使用for(;;)for无限循环,并且您的情况没有解决方案。这是我公司的开发约定允许禁用的极少数 4 级警告之一。

于 2009-12-22T13:51:01.337 回答
28

也许您的代码需要更多 owls

do { stuff(); } while (0,0)

或者更不上镜但也更少警告产生:

do { stuff(); } while ((void)0,0)
于 2009-12-22T15:51:25.480 回答
18

正如Michael BurrCarl Smotricz回答中指出的那样,对于 Visual Studio 2008+,您可以使用__pragma

#define MYMACRO(f,g)              \
  __pragma(warning(push))         \
  __pragma(warning(disable:4127)) \
  do { f; g; } while (0)          \
  __pragma(warning(pop))

\如果您希望宏不可读,可以将其放在一行(不带s)。

于 2011-01-27T12:37:31.930 回答
15

我有一个基于此处答案的模式,它适用于 clang、gcc 和 MSVC。我在这里发布它是希望它对其他人有用,因为这里的答案帮助我制定了它。

#ifdef WIN32
#  define ONCE __pragma( warning(push) ) \
               __pragma( warning(disable:4127) ) \
               while( 0 ) \
               __pragma( warning(pop) )
#else
#  define ONCE while( 0 )
#endif

我像这样使用它:

do {
   // Some stuff
} ONCE;

您也可以在宏中使用它:

void SomeLogImpl( const char* filename, int line, ... );    

#ifdef NDEBUG
#  define LOG( ... )
#else
#  define LOG( ... ) do { \
      SomeLogImpl( __FILE__, __LINE__, __VA_ARGS__ ); \
   } ONCE
#endif

这也适用于上面指出的情况,如果 F 在函数中使用 'ONCE':

#define F( x ) do { f(x); } ONCE
...
if (a==b) F(bar); else someFunc();

编辑:多年后,我意识到我忘记添加我实际上为这个宏编写的模式 - “switch-like-a-goto”模式:

do {
    begin_some_operation();

    if( something_is_wrong ) {
        break;
    }

    continue_big_operation();

    if( another_failure_cond ) {
        break;
    }

    finish_big_operation();
    return SUCCESS;
} ONCE;

cleanup_the_mess();
return FAILURE;

这为您提供了一个 try/finally-ish 结构,它比清理和返回代码的粗鲁 goto 更有条理。使用这个 ONCE 宏而不是 while(0) 会关闭 VS。

于 2013-05-04T18:25:26.723 回答
5

使用较新版本的 MS 编译器,您可以使用警告抑制:

#define MY_MACRO(stuff) \
    do { \
        stuff \
    __pragma(warning(suppress:4127)) \
    } while(0)

您也可以推送/禁用/弹出,但抑制是一种更方便的机制。

于 2014-09-11T19:00:17.717 回答
3

此编译器错误已在 Visual Studio 2015 Update 1 中修复,即使发行说明未提及它。

该错误在先前的答案之一中得到了解释:

摘要:在这种特殊情况下,此警告 (C4127) 是一个微妙的编译器错误。随意禁用它。

它旨在捕捉逻辑表达式在非显而易见的情况下评估为常量的情况(例如,if(a==a && a!=a),并且不知何故,它把 while(true) 和其他有用的构造变为无效.

于 2016-04-16T00:21:29.810 回答
3

这是另一种可能的方法,它避免了 C4127、C4548 和 C6319(VS2013 代码分析警告),并且不需要宏或编译指示:

static const struct {
    inline operator bool() const { return false; }
} false_value;

do {
    // ...
} while (false_value);

这会优化,并且在 GCC 4.9.2 和 VS2013 中编译时不会出现警告。在实践中,它可以进入命名空间。

于 2016-07-19T18:07:52.040 回答
2

该警告是由于while(false). 该站点提供了如何解决此问题的示例。来自站点的示例(您必须为您的代码重新工作):

#define MULTI_LINE_MACRO_BEGIN do {  
#define MULTI_LINE_MACRO_END \  
    __pragma(warning(push)) \  
    __pragma(warning(disable:4127)) \  
    } while(0) \  
    __pragma(warning(pop))

#define MULTI_LINE_MACRO \  
        MULTI_LINE_MACRO_BEGIN \  
            std::printf("Hello "); \  
            std::printf("world!\n"); \  
        MULTI_LINE_MACRO_END  

只需在 BEGIN 和 END 之间插入您的代码。

于 2012-08-20T18:54:16.550 回答
2

您可以使用

do {
    // Anything you like
} WHILE_FALSE;

之前定义WHILE_FALSE宏如下:

#define WHILE_FALSE \
    __pragma(warning(push))         \
    __pragma(warning(disable:4127)) \
    while(false)                    \
  __pragma(warning(pop))

在 MSVC++2013 上验证。

于 2016-08-24T08:03:54.473 回答
1

这个“while(0)”的东西是一个黑客,刚刚转身咬你。

您的编译器是否提供#pragmas 用于选择性地和本地关闭特定错误消息?如果是这样,那可能是一个明智的选择。

于 2009-12-22T13:45:48.787 回答
1

#define STUFF for (bool b = true; b;) do {f(); g(); b = false;} while (b)?

#define STUFF for (;;) {f(); g(); break;}?

于 2009-12-22T14:52:50.510 回答
1

对于要在表达式中使用的多语句宏,您可以使用逗号运算符而不是 do-while(0) 构造。所以而不是:

#define FOO(...)    do { Statement1; Statement2; Statement3; } while(0)

采用:

#define FOO(...)    (Statement1, Statement2, Statement3)

这独立于平台工作,并允许避免编译器警告(即使选择了最高警告级别)。请注意,在包含宏(第二个 FOO)的逗号中,最后一条语句(Statement3)的结果将是整个宏的结果。

于 2013-10-07T16:21:16.430 回答
0

我必须说,我从来没有打扰过宏中的 do..while 构造。我的宏中的所有代码本身都包含在大括号中,但没有 do-..while。例如:

#define F(x) \
    {           \
        x++;    \
    }           \

int main() {
    int a = 1;
    F(a);
    printf( "%d\n", a );
}

此外,我自己的编码标准(以及多年来的非正式实践)一直是让所有块,无论它们出现在哪里,都用大括号括起来,这也或多或少地解决了这个问题。

于 2009-12-22T13:50:32.040 回答
0

您可以使用#pragma 警告来:

  1. 保存状态
  2. 禁用警告
  3. 编写有问题的代码
  4. 将警告返回到之前的状态

(你需要一个 # 在编译指示之前,但是 SO 很难同时处理它们和格式化)

#pragma warning( push )
#pragma warning( disable: 4127 )
// Your code
#pragma warning( pop ) 

您想要推送/弹出警告而不是禁用/启用,因为您不想干扰可能选择打开/关闭警告的命令行参数(有人可能会使用命令行关闭警告,您这样做不想强迫它重新启动......上面的代码处理)。

这比全局关闭警告要好,因为您可以仅针对所需部分进行控制。您也可以将其作为宏的一部分。

于 2009-12-22T14:31:13.967 回答
0

有一个解决方案,但它会为您的代码添加更多循环。不要在 while 条件中使用显式值。

你可以这样:

文件1.h

extern const int I_am_a_zero;
#define MY_MACRO(foo,bar) \
do \
{ \
} \
while(I_am_a_zero);

变量 I_am_a_zero 应该在一些 .c 文件中定义。

无论如何,这个警告不会出现在 GCC 中:)

请参阅此相关问题

于 2009-12-22T16:24:07.097 回答
0

好吧,对我来说,以下工作没有 C4127 警告:

#define ALWAYS_TRUE(zzsome) ((##zzsome)==(##zzsome))

void foo()
{
    int a = 0;
    while( ALWAYS_TRUE(a) )
    {
    }
}

当然,编译器很聪明, zzsome 不应该是一个常数

于 2015-08-13T18:20:50.570 回答
0

这将禁用警告,编译器仍然可以优化代码:

static inline bool to_bool(const bool v) { return v; }

if (to_bool(0)) { // no warning here
    dead_code(); // will be compiled out (by most compilers)
}

do { something(); } while(to_bool(0)); // no extra code generated
于 2015-12-19T22:16:55.060 回答
0

我发现这是最短的版本

do {
  // ... 
}
while (([]() { return 0; })())  /* workaround for MSVC warning C4172 : conditional expression is constant */

还没有检查编译器是否对其进行了优化,但我猜是这样。

于 2016-02-09T10:09:08.823 回答
0

您可以将for循环用作:

for (;;) {
  // code
  break;
}

宏:

#define BEGIN \
  for (;;) {

#define END \
  break; }
于 2016-10-04T11:15:47.563 回答
-1

我会用

for(int i = 0; i < 1; ++i) //do once
{

}

这相当于

do
{
}while(0);

并且不会产生任何警告。

于 2012-10-19T08:54:03.443 回答
-1

请使用编译器开关/wd"4127"在您的项目中禁用此警告。

于 2020-02-03T09:47:30.630 回答