do...while(0)的用法

来源:互联网 发布:高维稀疏数据 编辑:程序博客网 时间:2024/05/29 08:40

1.在宏定义中的用法

使用do{…}while(0)构造后的宏定义不会受到大括号、分号等的影响,总是会按你期望的方式调用运行。
例如:

#define foo(x) bar(x); baz(x)

然后你可能这样调用:

foo(wolf);

这将被宏扩展为:

bar(wolf); baz(wolf);

这的确是我们期望的正确输出。下面看看如果我们这样调用:

if (!feral)    foo(wolf);

那么扩展后可能就不是你所期望的结果。上面语句将扩展为:

if (!feral)    bar(wolf);baz(wolf);

显而易见,这是错误的,也是大家经常易犯的错误之一。

几乎在所有的情况下,期望写多语句宏来达到正确的结果是不可能的。你不能让宏像函数一样行为——在没有do/while(0)的情况下。

如果我们使用do{…}while(0)来重新定义宏,即:

#define foo(x) do { bar(x); baz(x); } while (0)

现在,该语句功能上等价于前者,do能确保大括号里的逻辑能被执行,而while(0)能确保该逻辑只被执行一次,即与没有循环时一样。

对于上面的if语句,将会被扩展为:

if (!feral)    do { bar(wolf); baz(wolf); } while (0);

从语义上讲,它与下面的语句是等价的:

if (!feral) {    bar(wolf);    baz(wolf);}

这里你可能感到迷惑不解了,为什么不用大括号直接把宏包围起来呢?为什么非得使用do/while(0)逻辑呢?

例如,我们用大括号来定义宏如下:

#define foo(x)  { bar(x); baz(x); }

这对于上面举的if语句的确能被正确扩展,但是如果我们有下面的语句调用呢:

if (!feral)    foo(wolf);else    bin(wolf);

宏扩展后将变成:

if (!feral) {    bar(wolf);    baz(wolf);};else    bin(wolf);

大家可以看出,这就有语法错误了。

总结:Linux和其它代码库里的宏都用do/while(0)来包围执行逻辑,因为它能确保宏的行为总是相同的,而不管在调用代码中使用了多少分号和大括号。


2.消除goto语句

通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,当然,退出前先释放资源,我们的代码可能是这样:

Version1:

bool Execute(){   // 分配资源   int *p = new int;   bool bOk(true);   // 执行并进行错误处理   bOk = func1();   if(!bOk)    {      delete p;         p = NULL;      return false;   }   bOk = func2();   if(!bOk)    {      delete p;         p = NULL;      return false;   }   bOk = func3();   if(!bOk)    {      delete p;         p = NULL;      return false;   }   // ..........   // 执行成功,释放资源并返回    delete p;       p = NULL;    return true;   }

这里一个最大的问题就是代码的冗余,而且每增加一个操作,就需要做相应的错误处理,非常不灵活。于是我们想到了goto:
Version2:

bool Execute(){   // 分配资源   int *p = new int;   bool bOk(true);   // 执行并进行错误处理   bOk = func1();   if(!bOk) goto errorhandle;   bOk = func2();   if(!bOk) goto errorhandle;   bOk = func3();   if(!bOk) goto errorhandle;   // ..........   // 执行成功,释放资源并返回    delete p;       p = NULL;    return true;errorhandle:    delete p;       p = NULL;    return false;   }

代码冗余是消除了,但是我们引入了C++中身份比较微妙的goto语句,虽然正确的使用goto可以大大提高程序的灵活性与简洁性,但太灵活的东西往往是很危险的,它会让我们的程序捉摸不定,那么怎么才能避免使用goto语句,又能消除代码冗余呢,请看do…while(0)循环:

Version3:

bool Execute(){   // 分配资源   int *p = new int;   bool bOk(true);   do   {      // 执行并进行错误处理      bOk = func1();      if(!bOk) break;      bOk = func2();      if(!bOk) break;      bOk = func3();      if(!bOk) break;      // ..........   }while(0);    // 释放资源    delete p;       p = NULL;    return bOk;   }

使用break语句,十分漂亮。


3.参考

1.http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html
2.http://www.cnblogs.com/lanxuezaipiao/p/3535626.html

1 0