为什么在宏定义中要使用do { } while (0)?

21

可能是重复的问题:
为什么C/C++宏中有时会出现无意义的do/while和if/else语句?

我遇到了下面这样的代码:

#define ev_io_init(ev,cb,fd,events) \
do { \
  ev_init ((ev), (cb)); \
  ev_io_set ((ev),(fd),(events)); \
} while (0)

我想知道为什么作者在这里使用 do { } while (0)。 和这种方式有什么不同吗?
#define ev_io_init(ev,cb,fd,events) { \
  ev_init ((ev), (cb)); \
  ev_io_set ((ev),(fd),(events)); \
}

顺便说一下:这段代码来自 libev 的 ev_local.h 文件。

3个回答

33
考虑以下代码:if( something ) function1(); else function2(); 如果function1()是一个宏,仅使用{ }要求您在使用点处省略分号,但是do { } while(0)允许您使用与真实函数完全相同的语法。
(不使用任何块结构将生成完全错误的代码,当然)

22

将代码用循环括起来可以使预处理指令在if-else结构中执行多个语句而不会“破坏”它们。考虑以下示例:

#define DO_SOMETHING() a();b();c();

void foo()
{
    // This is ok...
    DO_SOMETHING();
}

void bar()
{
    // ...whereas this would trigger an error.
    if (condition)
       DO_SOMETHING();
    else
       blah();
}

第二个例子打破了if-else结构,因为三个语句后跟了一个else子句。要使其正确替换,DO_SOMETHING中的指令应该用do { ... } while(0)括起来。


4
当然,如果你使用裸的if语句像那样,你就应该预料到你的代码会崩溃... - Simon
2
@Simon 但是Linux内核编码风格建议我们对于单行块使用裸的if else语句吗?https://www.kernel.org/doc/Documentation/CodingStyle - CoderSpinoza
2
@CoderSpinoza 显然如此,如果我要在Linux内核上工作,那么我会遵循那种风格。但在其他地方,我会避免使用它。 - Simon
2
很多C代码仓库使用没有大括号的单行if语句,这是完全可以的。 - Elvis Teixeira

13

do{}while(0)允许你从循环中跳出:

do{
   expr1;
   foo();
   if ( cond )
      break;
   expr2;
   goo(); 
} while (0);

它与简单代码块{...}相同,除了您可以使用break语句在需要时中断执行。在简单代码块中无法做到这一点,除非您有多个检查,这可能会变得繁琐。由于条件while(0),它仍会被执行一次。


9
可以,但请不要这样做。 - moonshadow
1
它减少了if-else嵌套的数量。 - KRoy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接