C / C ++:如何使用do-while(0);构造没有编译器警告,如C4127?

时间:2009-12-22 13:42:18

标签: c++ c visual-c++ macros compiler-warnings

由于in this answer所述的原因,我经常在我的#defines中使用do-while(0)构造。此外,我正在尝试使用尽可能高的编译器警告级别来捕获更多潜在问题,并使我的代码更加健壮和跨平台。所以我通常使用带有gcc的-Wall和带有MSVC的/Wall

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

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

我应该怎么做这个警告?

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

20 个答案:

答案 0 :(得分:45)

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

深度:

它意味着捕获逻辑表达式在非显而易见的情况下评估为常量的情况(例如if(a==a && a!=a),并且不知何故,它将while(true)和其他有用的结构转换为无效。

如果您希望启用此警告,Microsoft建议使用for(;;)进行无限循环,并且您的案例没有解决方案。这是我公司的开发惯例允许禁用的极少数Level-4警告之一。

答案 1 :(得分:27)

也许您的代码需要more owls

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

或者较少上镜而且警告产生较少:

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

答案 2 :(得分:17)

Michael Burr'Carl Smotricz中注明answer,对于Visual Studio 2008+,您可以使用__pragma

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

如果您希望宏不可读,可以将它放在一行(没有\)。

答案 3 :(得分: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结构,它比你的清理和结构更加结构化。返回代码。使用此ONCE宏而不是while(0)会关闭VS.

答案 4 :(得分:4)

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

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

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

答案 5 :(得分: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中编译而不会发出警告。在实践中,它可以进入命名空间。

答案 6 :(得分:3)

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

但是之前的答案之一解释了这个错误:

  

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

     

这是为了捕捉逻辑表达式在非显而易见的情况下评估为常数的情况(例如,if(a == a&& a!= a),并且某种程度上,它转向while(true)和其他有用的结构变成无效。

答案 7 :(得分: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上验证。

答案 8 :(得分:2)

警告是由while(false)引起的。这个site给出了如何解决此问题的示例。网站示例(您必须为代码重新处理):

#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之间插入代码。

答案 9 :(得分:1)

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

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

答案 10 :(得分:1)

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

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

答案 11 :(得分:1)

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

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

使用:

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

这与平台无关,可以避免编译器警告(即使选择了最高警告级别)。 请注意,在逗号包含宏(第二个FOO)中,最后一个语句(Statement3)的结果将是整个宏的结果。

答案 12 :(得分:0)

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

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

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

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

答案 13 :(得分:0)

您可以将for循环用作:

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

宏:

#define BEGIN \
  for (;;) {

#define END \
  break; }

答案 14 :(得分:0)

我发现这是最短的版本

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

Haven没有检查它是否被编译器优化了,但我猜它是。

答案 15 :(得分: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

答案 16 :(得分:0)

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

你可以这样:

file1.h

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

变量I_am_a_zero应该在某个.c文件中定义。

无论如何,这个警告不会出现在海湾合作委员会中:)

请参阅此related question

答案 17 :(得分:0)

您可以使用#pragma警告:

  1. 保存状态
  2. 禁用警告
  3. 撰写有问题的代码
  4. 将警告恢复到之前的状态
  5. (在pragma之前你需要一个#,但是SO很难同时处理它们并进行格式化)

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

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

    这比全局关闭警告要好,因为您只能为所需的部分控制它。你也可以把它作为宏的一部分。

答案 18 :(得分:-1)

我用

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

}

这相当于

do
{
}while(0);

并且没有警告。

答案 19 :(得分:-1)

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