一、开始
循环在程序中是不可少的,要么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:
f1f2---------------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