do...while(0)的用法

本文探讨了宏定义中do{...}

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值