do while(0)在宏定义中的应用

来源:互联网 发布:软件测试作业 编辑:程序博客网 时间:2024/05/02 07:13
看linux源码时,经常发现有宏定义用
#define XYZ \
do{...} while(0)
这是一个奇怪的循环,它根本就只会运行一次,为什么不去掉外面的do{..}while结构呢?原来这也是非常巧妙的技巧。在工程中可能经常会引起麻烦,而上面的定义能够保证这些麻烦不会出现。下面是解释:

假设有这样一个宏定义
#define macro(condition) \
if(condition) dosomething()

现在在程序中这样使用这个宏:
if(temp)
    macro(i);
else
    doanotherthing();

一切看起来很正常,但是仔细想想。这个宏会展开成:
if(temp)
    if(i) dosomething();
else
    doanotherthing();

这时的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

好了 这样就一切太平了。

原创粉丝点击