do{...}while(0)

近期在研究linux内核中关于rcu部分的代码的时候,看到了很多宏定义使用了do{…}while(0)。初看之下感觉意义仅仅在于让代码执行一次,遂搜索之,方知道其实这个技巧在某些时候非常有用,特此记录。

最常见的情况是为了辅助定义复杂的宏,避免引用的时候出错:

举例来说,假设你需要定义这样一个宏:

#define DOSOMETHING()\
               foo1();\
               foo2();

这个宏的本意是,当调用DOSOMETHING()时,函数foo1()和foo2()都会被调用。但是如果你在调用的时候这么写:

if(a>0)
    DOSOMETHING();

因为宏在预处理的时候会直接被展开,你实际上写的代码是这个样子的:

if(a>0)
    foo1();
foo2();

这就出现了问题,因为无论a是否大于0,foo2()都会被执行,导致程序出错。
那为什么不用大括号来包围,而需要使用do-while呢。
看例子:
我们用大括号来定义宏如下:

#define foo(x)  { bar(x); baz(x); }

这对于上面举的if语句的确能被正确扩展,但是如果我们有下面的语句调用呢:


if (!feral)
    foo(wolf);
else
    bin(wolf);

宏扩展后将变成:


if (!feral) {
    bar(wolf);
    baz(wolf);
};
else
    bin(wolf);

大家可以看出,这就有语法错误了。
所以,很多人才采用了do{…}while(0);

PS:这里我一开始有疑惑为什么,后来查了多篇博客之后,得出结论是因为大部分人写完函数foo(wolf)后习惯会加分号;而如果不加分号的话其实应该是没有问题的。
PPS:想到这一点后又突然想起来,那平时加分号,宏替换后不就会有俩分号嘛,犹豫了一会儿突然想起来,末尾分号多于一个并不会影响编译啊(躺)
PPPS:如果上面那两段是我理解错了,恳请评论指出,感激不尽
另外一种被提及地不多的情况就是,可以避免使用GOTO,用break做跳出.
例如当你执行一段代码到一半,想跳过剩下的一半的时候,如果你正处于do while循环中,能用break轻松达到这个目的。

你可能感兴趣的:(linux,C)