宏定义中使用do{}while(0)的好处 (转载)

宏定义中使用do{}while(0)的好处

 

#define MACRO_NAME(para) do{macro content}while(0)

的格式,总结了以下几个原因:

1,空的宏定义避免warning:

#define foo() do{}while(0)

2,存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。

3,如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:

#define foo(x) /action1(); /action2();

在以下情况下:

if(NULL == pPointer)   foo();

就会出现action1和action2不会同时被执行的情况,而这显然不是程序设计的目的。

4,以上的第3种情况用单独的{}也可以实现,但是为什么一定要一个do{}while(0)呢,看以下代码:

#define switch(x,y) {int tmp; tmp="x";x=y;y=tmp;}if(x>y)  switch(x,y);else       //error, parse error before else  otheraction();

在把宏引入代码中,会多出一个分号,从而会报错。

//------------------------------------------------

使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,
从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无
用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低

在C++中,有三种类型的循环语句:for, while, 和do...while,

但是在一般应用中作循环时, 我们可能用for和while要多一些,do...while相对不受重视。
但是,最近在读我们项目的代码时,却发现了do...while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。

1. do...while(0)消除goto语句。

通常,如果在一个函数中开始要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,

当然,退出前先释放资源,我们的代码可能是这样:

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:
version 2

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;

}

2 宏定义中的do...while(0)
如果你是C++程序员,我有理由相信你用过,或者接触过,至少听说过MFC, 在MFC的afx.h文件里面,

你会发现很多宏定义都是用了do...while(0)或do...while(false), 比如说:

#define AFXASSUME(cond)       do { bool __afx_condVal=!!(cond); ASSERT(__afx_condVal);__analysis_assume(__afx_condVal); } while(0) 

粗看我们就会觉得很奇怪,既然循环里面只执行了一次,

我要这个看似多余的do...while(0)有什么意义呢? 当然有!

为了看起来更清晰,这里用一个简单点的宏来演示:

#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...

其else分支就无法通过编译了(原因同上),所以采用do...while(0)是做好的选择了。

也许你会说,我们代码的习惯是在每个判断后面加上{}, 就不会有这种问题了,也就不需要do...while了,如:

if(...) {}else{}

诚然,这是一个好的,应该提倡的编程习惯,但一般这样的宏都是作为library的一部分出现的,

而对于一个library的作者,他所要做的就是让其库具有通用性,强壮性,

因此他不能有任何对库的使用者的假设,如其编码规范,技术水平等

来自:http://blog.csdn.net/liliangbao/article/details/4163440

http://www.cnblogs.com/bastard/archive/2011/11/24/2262195.html

时间: 2024-07-30 13:38:13

宏定义中使用do{}while(0)的好处 (转载)的相关文章

宏定义中的#,##,...,do{}while(0),__VA_ARGS__

宏定义中的#,## 1.在一个预处理器宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组 #define syslog(a) fprintf(stderr,"Warning: " #a"\n"); 2.简单的说,"## "是一种分隔连接方式,它的作用是先分隔,然后进行强制连接 举列 -- 试比较下述几个宏定义的区别 #define A1(name, type)  type name_##type##_type 或 #define A

C 语言宏定义中使用do...while(0)

最近看到C语言的项目中在宏定义中使用了do...while(0),感到是个知识点,特来总结. 先看这句话:do{...}while(0)在C中是唯一的构造程序,让你定义的宏总是以相同的方式工作,这样不管怎么使用宏(尤其在没有用大括号包围调用宏的语句),宏后面的分号也是相同的效果.这句话的意思是说,在宏定义中使用do...while(0)包含的所有语句是一个代码块,不会受到{}:的影响. 看一个例子: #include <stdio.h> #include <stdlib.h> #d

do{}while(0)在宏定义中作用

在开源代码中看到,宏定义经常这样用 #define some() do { do_somt_thing(); } while (0) 为什么这样用? 可以试一下,假如一个普通宏定义 #define some(x) Fun1(x);Fun2(x) if(condition) some(x); 变为 if(condition) Fun1(x); Fun2(x); 这样直接加个花括号不久行了,为什么还用do......while()?假如加上花括号 #define some(x) {Fun1(x);F

do...while(0)在宏定义中的巧妙用法

大家都知道,do-while(condition)可以表示循环,但你有没有遇到在一些宏定义中可以不用循环的地方,也用到了 do-while.比如: #define DELETE_POINTER(p) do { if(NULL != p) delete p; p = NULL; }while(0) 这时,do-while(0)的功能就不仅仅是循环了,这是do..while(0)的一种巧妙用法.它有以下几种功能: 1.在后面要加分号,使调用如同函数:调用如下: int* p = new int(5)

#和##在宏定义中使用问题

转自:http://www.linuxidc.com/Linux/2014-06/102925.htm 有一道经典的C语言问题,关于宏定义中#和##符号的使用和宏定义展开问题 程序如下: #include <stdio.h>#define f(a,b) a##b#define g(a)  #a#define h(a) g(a) int main(){        printf("%s\n", h(f(1,2)));        printf("%s\n&quo

define宏定义中的#,##,@#及\符号

define宏定义中的#,##,@#及\符号 在#define中,标准只定义了#和##两种操作.#用来把参数转换成字符串,##则用来连接两个前后两个参数,把它们变成一个字符串. 1.# (stringizing)字符串化操作符.其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串.其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前. 如: #define example(instr) printf("the input string is:\t%s\n",#

宏定义中#和##的使用

1. # #的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号 1 #define WARN_IF(EXP) / 2 do{ if (EXP) / 3 fprintf(stderr, "Warning: " #EXP "/n"); } / 4 while(0) 那么实际使用中会出现下面所示的替换过程: 1 WARN_IF (divider == 0); 被替换为 1 do { 2

C宏定义中的连接符&quot;##&quot;和字符串化操作符&quot;# &quot;及变参宏&quot;...&quot;

C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念). 1.宏定义中字符串化操作符#: #的功能是将其后面的宏参数进行字符串化操作,意思就是对它所应用的宏变量通过替换后在其左右各加上一个双引号. 例如: #define WARN_IF(EXPR)do { if (EXPR) fprintf(stderr, "Warning: " #EXPR "\n");} while(0) 上面代码中的反斜线\主要用来转译换行

宏定义中##和#的作用

解释1 : 定义中##和#的作用内核中有很多的宏定义,在宏定义define中经常看到两个字符串##和#,这里把它的用法做一下说明:1. ####是一个连接符号,用于把参数连在一起例如:> #define FOO(arg) my##arg则> FOO(abc)相当于 myabc2.#:是"字符串化"的意思.出现在宏定义中的#是把跟在后面的参数转换成一个字符串例如:> #define STRCPY(dst, src) strcpy(dst, #src)则> STRC