do…while(0)的妙用

来源:互联网 发布:小意思 mac 编辑:程序博客网 时间:2024/06/05 22:35

一、开始

循环在程序中是不可少的,要么for, while...do,do...while, 但它都有自己的循环条件,在一些Linux的库中,MFC中常常会有do...while(0),或者do...while(FALSE)的宏,有什么用呢?不是用来循环,是用来提高代码的健壮性。闲来无事,总结一下。

 

二、妙用总结

1.使程序结构简单

  1: //----------------------------------------- 
  2: //说明 : do...while(0)测试 
  3: //环境 : VS2008 
  4: //作者 : http://pppboy.blog.163.com
  5: //日期 : 2011.11.19 
  6: //---------------------------------------- 
  7:  
  8: #include "stdafx.h" 
  9: #include <iostream> 
 10:  
 11: using namespace std; 
 12:  
 13: void fun1() 
 14: { 
 15:   cout << "F1\n"; 
 16: } 
 17:  
 18: int main(int argc, char* argv[]) 
 19: { 
 20:   int a = 1; 
 21:   int b = 2; 
 22:   int c = 3; 
 23:   int d = 4; 
 24:  
 25:   //使用方法1 
 26:   //n多括号之类 
 27:   if (a < b && b < c) 
 28:   { 
 29:     if (c < d)   
 30:     { 
 31:       fun1(); 
 32:     } 
 33:   } 
 34:  
 35:   //使用do...while(0) 
 36:   //结构更清些 
 37:   do  
 38:   { 
 39:     if (a >= b) break; 
 40:     if (b >= c) break; 
 41:     if (c >= d) break; 
 42:  
 43:     fun1(); 
 44:  
 45:   } while (0); 
 46:  
 47:   system("pause"); 
 48:   return 0; 
 49: }

 

2.代替域名符{ },防止非单行宏语句展开出错

有以下几种情况:

(1)宏展开后有分号导致出现错误

(用了宏,在它的后面你是写不写分号?)

  1: #include "stdafx.h" 
  2: #include <iostream> 
  3:  
  4: using namespace std; 
  5:  
  6: #define ASSERT1(val) { if(!val) return -1;} 
  7: #define ASSERT2(val) do{ if(!val) return -1;}while(0) 
  8:  
  9: void fun() 
 10: { 
 11:   cout << "fun...\n"; 
 12: } 
 13:  
 14: int main(int argc, char* argv[]) 
 15: { 
 16:   int* pVal = NULL; 
 17:   bool bCon(true); 
 18:  
 19:   if(bCon) 
 20:  
 21:     //error C2181: 没有匹配 if 的非法 else 
 22:     //ASSERT1(pVal);  
 23:  
 24:     //正确 
 25:     ASSERT2(pVal); 
 26:   else 
 27:     fun(); 
 28:  
 29:   system("pause"); 
 30:   return 0; 
 31: }

使用ASSERT1就会有错误。

但是,取了ASSERT1后面的分号就不会有问题,原因就是展开后的分号。

if(...) 
{...} 
; 
else 
{...} 

 

(2) 因为没有{},而有判断语句导致宏第一行以外的全部执行

do...while(0)保证宏作为一个整体来是实现

  1:  
  2: #include "stdafx.h" 
  3: #include <iostream> 
  4:  
  5: using namespace std; 
  6:  
  7: void fun1() 
  8: {   
  9:   cout << "f1 \n";  
 10: } 
 11: void fun2() 
 12: {   
 13:   cout << "f2 \n"; 
 14: } 
 15:  
 16: //这个宏的原本意义是fun1和fun2都要执行 
 17: #define foo    \ 
 18:     fun1();  \ 
 19:     fun2(); 
 20:  
 21: //用do...while(0) 
 22: #define foo2  \ 
 23: do        \ 
 24: {         \ 
 25:   fun1();    \ 
 26:   fun2();    \ 
 27: } while (0); 
 28:  
 29: int main(int argc, char* argv[]) 
 30: { 
 31:    //没有判断两个都输出 
 32:   foo; 
 33:  
 34:   cout << "---------------\n"; 
 35:   
 36:   //前面有了判断,展开后无论如何都会执行fun2 
 37:   if (false) 
 38:     foo; 
 39:  
 40:   cout << "---------------\n"; 
 41:   //有了判断用了do...while(0),整体都不会执行  
 42:   if (false) 
 43:     foo2; 
 44:   system("pause"); 
 45:   return 0; 
 46: }

out:

f1 
f2 
--------------- 
f2 
--------------- 
请按任意键继续. . .

可以看到,有了if后,foo宏展开后就是:

if(false)

fun1();

fun2();

很明显,fun2无论如何都会被执行,而用了do...while(0)的宏就不一样了。

 

(3)当然,不用else,或者在if后再使用一个{}也会没有问题,这是个好习惯。

不过对库的作者来说,你定义的宏得保证任何人都玩得起,你做的东西就是给别人用的,不要把责任推到别人头上。

  1: if(...) 
  2: {...} 
  3: else 
  4: {...} 
  5: 

 

3.消除goto语句

  1: #include "stdafx.h" 
  2: #include <iostream> 
  3:  
  4: using namespace std; 
  5:  
  6: bool fun1() 
  7: { 
  8:   cout << "fun1...\n"; 
  9:   return true; 
 10: } 
 11:  
 12: bool fun2() 
 13: { 
 14:   cout << "fun2...\n"; 
 15:   return true; 
 16: } 
 17:  
 18: //第一个版本,冗余啊!! 
 19: //仅当例子,不讨论智能指针和try...catch的问题 
 20: bool Exe1() 
 21: { 
 22:   int* p = new int; 
 23:   bool bOK(true); 
 24:  
 25:   bOK = fun1(); 
 26:   if (!bOK) 
 27:   { 
 28:     delete p; 
 29:     p = NULL; 
 30:     return false; 
 31:   } 
 32:  
 33:   bOK = fun2(); 
 34:   if (!bOK) 
 35:   { 
 36:     delete p; 
 37:     p = NULL; 
 38:     return false; 
 39:   } 
 40:  
 41:   // 执行成功,释放资源并返回 
 42:   delete p;    
 43:   p = NULL; 
 44:   return true; 
 45: } 
 46:  
 47: //版本2,goto 
 48: //混乱啊!! 
 49: bool Exe2() 
 50: { 
 51:   // 分配资源 
 52:   int *p = new int; 
 53:   bool bOk(true); 
 54:  
 55:   // 执行并进行错误处理 
 56:   bOk = fun1(); 
 57:   if(!bOk) goto errorhandle; 
 58:   bOk = fun2(); 
 59:   if(!bOk) goto errorhandle; 
 60:  
 61:   // 执行成功,释放资源并返回 
 62:   delete p;    
 63:   p = NULL; 
 64:   return true; 
 65:  
 66: errorhandle: 
 67:   delete p;    
 68:   p = NULL; 
 69:   return false; 
 70:  
 71: } 
 72:  
 73: //版本3 do...while(0) 
 74: //清晰吧! 
 75: bool Exe3() 
 76: { 
 77:   // 分配资源 
 78:   int *p = new int; 
 79:   bool bOk(true); 
 80:   do 
 81:   { 
 82:     // 执行并进行错误处理 
 83:     bOk = fun1(); 
 84:     if(!bOk) break; 
 85:     bOk = fun2(); 
 86:     if(!bOk) break; 
 87:   }while(0); 
 88:  
 89:   // 释放资源 
 90:   delete p;    
 91:   p = NULL; 
 92:   return bOk; 
 93: } 
 94:  
 95:  
 96: int main(int argc, char* argv[]) 
 97: { 
 98:   Exe1(); 
 99:   Exe2(); 
100:   Exe3(); 
101:  
102:   system("pause"); 
103:   return 0; 
104: } 
105: 

 

4.消除空语句在编译时候会出现警告,用#define FOO do { } while(0).

ps:仅当参考,不一定正确。

 

5.减少一个操作的域范围

e.g.

  1:  
  2: #include "stdafx.h" 
  3: #include <iostream> 
  4:  
  5: using namespace std; 
  6:  
  7: class A 
  8: { 
  9: public: 
 10:   A() 
 11:   { 
 12:     cout << "A Constructor- 智能加锁\n"; 
 13:   } 
 14:   ~A() 
 15:   { 
 16:     cout << "A Destructor- 智能解锁 \n"; 
 17:   } 
 18:  
 19:   int getNum()  
 20:   { 
 21:     return 1; 
 22:   } 
 23: }; 
 24:  
 25:  
 26: int main(int argc, char* argv[]) 
 27: { 
 28:   int n(0); 
 29:  
 30:   //减小了A的使用域,我们的目的只是得到a对象的一个值 
 31:   //a在while时就已经析构 
 32:   do  
 33:   { 
 34:     A a; //@1 
 35:     n = a.getNum(); 
 36:   } while (0); 
 37:  
 38:   cout << "n = " << n << endl; 
 39:  
 40:   //这个a1的域范围更大 
 41:   cout << "-----------------------\n"; 
 42:   A a1; 
 43:   n = a1.getNum(); 
 44:   cout << "n = " << n << endl; 
 45:  
 46:   system("pause"); 
 47:   return 0; 
 48: } 
 49: 

out:

A Constructor- 智能加锁 
A Destructor- 智能解锁 
n = 1 
----------------------- 
A Constructor- 智能加锁 
n = 1 
请按任意键继续. . .

可以看到,这个do...while(0)在这里减小了域的范围。上面的do...while(0)当然可以只用一个{}就可以,这样也会把范围放到{}里面,但这样总看起来有那么些不伦不类,说不定哪天谁没看清就给你写的{}删除了,那就是个无法找到的悲剧了。

上面只是一个例子,可能更多的是用在一个线程函数里面。比如我们只需要加锁取一个值,之后还有多其它低效率的操作,这时取完数据可以立即把释放锁,让别的线程使用。而假如我们用了智能锁的方法,就是在A类的析构的时候解锁,这时候A就可以当成一个临时变量,在{}结束的时候让它智能解锁。

不写do也可以,只写{}也可以,但都不如写do{...}while(0)让人明显地明白你这个是干啥的。

 

三、参考

文章参考了网上其它文章,现给出链接,

http://www.rtems.com/ml/rtems-users/2001/august/msg00086.html

http://www.yqshare.com/the-ues-of-do-while0.html

http://tieba.baidu.com/f?kz=263292825

http://cppkey.com/archives/336


原创粉丝点击