#define
do{...}
假设有这样一个宏定义
#define
if(condition)
现在在程序中这样使用这个宏:
if(temp)
else
一切看起来很正常,但是仔细想想。这个宏会展开成:
if(temp)
else
这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。
为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0) 这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。
此外,这是为了含多条语句的宏的通用性。因为默认规则是宏定义最后是不能加分号的,分号是在引用的时候加上的。
在MFC的afx.h文件里面, 你会发现很多宏定义都是用了do...while(0)或do...while(false)。
为了看起来更清晰,这里用一个简单点的宏来演示:
#define SAFE_DELETE(p) do{ delete p; p = NULL } while(0)
假设这里去掉do...while(0),
#define SAFE_DELETE(p) delete p; p = NULL;
那么以下代码:
if(NULL != p) SAFE_DELETE(p)
else ...do sth...
就有两个问题,
1) 因为if分支后有两个语句,else分支没有对应的if,编译失败。
2) 假设没有else, SAFE_DELETE中的第二个语句无论if测试是否通过,会永远执行。
你可能发现,为了避免这两个问题,我不一定要用这个令人费解的do...while, 我直接用{}括起来就可以了
#define SAFE_DELETE(p) { delete p; p = NULL;}
的确,这样的话上面的问题是不存在了,但是我想对于C++程序员来讲,在每个语句后面加分号是一种约定俗成的习惯,这样的话,以下代码:
if(NULL != p) SAFE_DELETE(p);
else ...do sth...
由于if后面的大括号后面加了一个分号,导致其else分支就无法通过编译了(原因同上),所以采用do...while(0)是做好的选择了。
如果使用名家的方法
#define SAFE_FREE(p) do {free(p);p=NULL;} while(0)
那么
if(NULL!=p)
SAFE_FREE(p);
else
do something
展开为
if(NULL!=p)
do
{ free(p);p=NULL;}
while(0);
else
do something
好了 这样就一切太平了。

本文深入解析Linux源码中常见的一种宏定义使用方式——包含do{}
1092

被折叠的 条评论
为什么被折叠?



