do...while(0)解析

来源:互联网 发布:布尔人 知乎 编辑:程序博客网 时间:2024/06/06 12:23

为什么在内核中碰到很多  #define ...  do{...} while(0) ?

有以下几点原因:

1、空语句在编译的时候会出现警告,所以有必要用#define FOO do{ } while(0)

2、给定一个基本块,可以在里面定义局部变量

3、为了能够在条件语句中使用复杂的宏定义。例如下面这段代码:

[cpp] view plain copy
  1. #define FOO(x) \  
  2.         printf("arg is %s\n", x); \  
  3.         do_something_useful(x);  
如果这样用:

[cpp] view plain copy
  1. if (blah == 2)  
  2.     F00(blah);  
宏展开之后为

[cpp] view plain copy
  1. if (blah == 2)  
  2.         printf("arg is %s\n", blah);  
  3.         do_something_useful(blah);;  

这样,if条件之后包含了printf()语句,而do_something_useful()调用不能按照预期那样工作。而是用do {...} while(0)定义后,就会展开成以下语句:

[cpp] view plain copy
  1. if (blah == 2)  
  2. do{  
  3.               printf("arg is %s\n", blah);  
  4.               do_something_useful(blah);  
  5.   
  6. }while(0);  
这是我们所期望的。

如果你希望定义一个包含多行语句和一些局部变量的时候. 一般的定义方式只能这样:

[cpp] view plain copy
  1. #define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }  
然而在某些情况下,这样并不能正常工作. 下面是包含两个分支的if语句:
[cpp] view plain copy
  1. if (x > y)  
  2.  exch(x,y);          // Branch 1  
  3. else  
  4.  do_something();     // Branch 2  
但这样却只能展开成单分支的if语句,如下:

[cpp] view plain copy
  1. if (x > y) {                // 单分支if  
  2.  int tmp;  
  3.  tmp = x;  
  4.  x = y;  
  5.  y = tmp;  
  6. }  
  7. ;                           // 空语句  
  8.  else                        // 错误!!! "parse error before else"  
  9.  do_something();  
问题是由于在语句块后直接加入分号(;)引起的. 解决办法是将语句块放入 do 和 while (0)中间.这样就得到了一条单语句, 而不是被编译器判断为语句块.现在的if语句如下:
[cpp] view plain copy
  1. if (x > y)  
  2.   do {  
  3.   int tmp;  
  4.   tmp = x;  
  5.   x = y;  
  6.   y = tmp;  
  7.   } while(0);  
  8.    else  
  9.   do_something();  
假设有这样一个宏定义  
[cpp] view plain copy
  1. #define  macro(condition)  if(condition)  dosomething();    
现在在程序中这样使用这个宏:  
[cpp] view plain copy
  1. if(temp)    
  2.      macro(i);    
  3. else    
  4.      doanotherthing();    

一切看起来很正常,但是仔细想想。这个宏会展开成:  
[cpp] view plain copy
  1. if(temp)    
  2.      if(condition)  dosomething();    
  3. else      
  4.      doanotherthing();    

这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。  

为了避免这个错误,我们使用do{….}while(0)  把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。