宏定义中使用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的作者,他所要做的就是让其库具有通用性,强壮性,

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

 

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

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

    define宏定义中的#,##,@#及\符号 在#define中,标准只定义了#和##两种操作.#用来把参数转换成字符串,##则用来连接两个前后两个参数,把它们变成一个字符串. 1.# (string ...

  2. 宏定义中的##操作符和... and _ _VA_ARGS_ _

    1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...

  3. C语言在宏定义中使用语句表达式和预处理器运算符

    语句表达式的亮点在于定义复杂功能的宏.使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞.下面以一个简单的最小值的宏为例子一步步说明. 1.灰常简单的么,使用条件运算符 ...

  4. C语言宏定义中的#和##的作用【转】

    本文转载自:http://my.oschina.net/shelllife/blog/123202 在宏定义中#和##的作用是:前者将宏定义的变量转化为字符串:后者将其前后的两个宏定义中的两个变量无缝 ...

  5. #define宏定义中## #@ # \ 符号使用

    C/C++ 宏命令的神奇用法. 先看下面三条语句: #define Conn(x,y)     x##y#define ToChar(x)     #@x#define ToString(x)    ...

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

    如果你是一名C程序员,你肯定很熟悉宏,它们非常强大,如果正确使用可以让你的工作事半功倍.然而,如果你在定义宏时很随意没有认真检查,那么它们可能使你发狂,浪费N多时间.在很多的C程序中,你可能会看到许多 ...

  7. C do {...} while (0) 在宏定义中的作用

    如果你是一名C程序员,你肯定很熟悉宏,它们非常强大,如果正确使用可以让你的工作事半功倍.然而,如果你在定义宏时很随意没有认真检查,那么它们可能使你发狂,浪费N多时间.在很多的C程序中,你可能会看到许多 ...

  8. C语言可变参数在宏定义中的应用

    在C语言的标准库中,printf.scanf.sscanf.sprintf.sscanf这些标准库的输入输出函数,参数都是可变的.在调试程序时,我们可能希望定义一个参数可变的输出函数来记录日志,那么用 ...

  9. C在宏定义中使用的语言可变参数

    于C标准库的语言,printf.scanf.sscanf.sprintf.sscanf入输出函数,參数都是可变的.在调试程序时.我们可能希望定义一个參数可变的输出函数来记录日志,那么用可变參数的宏是一 ...

随机推荐

  1. mysql数据库在Navicat Premium连接的时候出现1862错误

    navicat连接打开时报1862的错误, 很就没有连接mysql看看了,今天连接的时候发现本机的mysql链接不上了,在cmd中执行动作的时候也会叫你去set password,做设置密码的动作时会 ...

  2. dedecms _ 当前位置问题的代码

    {dede:field name='position' runphp='yes'} $tc=" > "; $tf=split($tc,@me); $tn=count($tf) ...

  3. nsfocus-笔试题

    1.描述sendmail原理及通讯机制 sendmail程序接受到待发邮件后,通过关键字@判断邮件的格式是否符合要求,匹配成功后提取邮件后缀域名信息并查询DNS数据库相关MX(邮件专用)记录,若有匹配 ...

  4. 关于css中overflow的一些理解

    在做移动端开发的时候,遇到过这么个问题:要把图片进行放大,但有时候图片比较长,一个手机的版面看不了,于是需要用到overflow的属性,刚开始用了overflow-y:scroll, 于是问题来了,如 ...

  5. Zookeeper,也要接触起来啦

    分布式的东东,就是部署也方便,但管理,想法,大集群应用是要点...! 参考如下URL简单实现 ,以后应用时多留意. http://blog.csdn.net/shirdrn/article/detai ...

  6. IntelliJ IDEA: maven & jetty 开发 java web

    之前使用eclipse + maven + jetty开发java web应用,本着no zuo no gain的想法, 折腾了一下Intellj idea下开发环境的搭建,顺带学习了maven re ...

  7. url中的jsessionid解释

    (1) 这是一个保险措施 因为Session默认是需要Cookie支持的 但有些客户浏览器是关闭Cookie的 这个时候就需要在URL中指定服务器上的session标识,也就是5F4771183629 ...

  8. Xmind 快捷键

    Xmind 快捷键 快捷键(Windows) 快捷键(Mac) 描述 Ctrl+N Command+N 建立新工作簿 Ctrl+O Command+O 开启工作簿 Ctrl+S Command+S 储 ...

  9. Ext.window的close的问题

    以前每次都是用的hide,关闭后隐藏窗体,下一次点击再打开,这种方法在我的随笔里面有,可是现在遇到一个问题,我的窗体里面有个formpanel,formpanel每一项都有一个默认值,意思就是修改的时 ...

  10. 基本NT式驱动代码结构

    #include <ntddk.h> void DriverUnload(IN PDRIVER_OBJECT DriverObject);NTSTATUS MyCreateClose(IN ...