c++中do{...}while(0)的意义和用法

linux内核和其他一些开源的代码中,经常会遇到这样的代码:

do{
 ...
}while(0)
这样的代码一看就不是一个循环,do..while表面上在这里一点意义都没有,那么为什么要这么用呢?

实际上,do{...}while(0)的作用远大于美化你的代码。查了些资料,总结起来这样写主要有以下几点好处:

1、辅助定义复杂的宏,避免引用的时候出错:

举例来说,假设你需要定义这样一个宏:

代码如下:

#define DOSOMETHING()\
               foo1();\
               foo2();

这个宏的本意是,当调用DOSOMETHING()时,函数foo1()和foo2()都会被调用。但是如果你在调用的时候这么写:

代码如下:

if(a>0)
    DOSOMETHING();

因为宏在预处理的时候会直接被展开,你实际上写的代码是这个样子的:

代码如下:

if(a>0)
    foo1();
foo2();

这就出现了问题,因为无论a是否大于0,foo2()都会被执行,导致程序出错。

那么仅仅使用{}将foo1()和foo2()包起来行么?

我们在写代码的时候都习惯在语句右面加上分号,如果在宏中使用{},代码里就相当于这样写了:“{...};”,展开后就是这个样子:

代码如下:

if(a>0)
{
    foo1();
    foo2();
};

这样甚至不会编译通过。所以,很多人才采用了do{...}while(0);

代码如下:

#define DOSOMETHING() \
        do{ \
          foo1();\
          foo2();\
        }while(0)\
...
 
if(a>0)
    DOSOMETHING();
...

这样,宏被展开后,才会保留初始的语义。GCC提供了Statement-Expressions用以替代do{...}while(0);

所以你也可以这样定义宏:

代码如下:

#define DOSOMETHING() ({\
        foo1(); \
        foo2(); \
})

2、避免使用goto对程序流进行统一的控制:

有些函数中,在函数return之前我们经常会进行一些收尾的工作,比如free掉一块函数开始malloc的内存,goto一直都是一个比较简便的方法:

代码如下:

int foo()
{
    somestruct* ptr = malloc(...);
 
    dosomething...;
    if(error)
    {
        goto END;
    }
 
    dosomething...;
    if(error)
    {
        goto END;
    }
    dosomething...;
 
END:
    free(ptr);
    return 0;
 
}

由于goto不符合软件工程的结构化,而且有可能使得代码难懂,所以很多人都不倡导使用,那这个时候就可以用do{}while(0)来进行统一的管理:

代码如下:

int foo()
{
    somestruct* ptr = malloc(...);
 
    do{
        dosomething...;
        if(error)
        {
            break;
        }
 
        dosomething...;
        if(error)
        {
            break;
        }
        dosomething...;
    }while(0);
 
    free(ptr);
    return 0;
 
}

这里将函数主体使用do()while(0)包含起来,使用break来代替goto,后续的处理工作在while之后,就能够达到同样的效果。

3、避免空宏引起的warning

内核中由于不同架构的限制,很多时候会用到空宏,在编译的时候,空宏会给出warning,为了避免这样的warning,就可以使用do{}while(0)来定义空宏:

#define EMPTYMICRO do{}while(0)
4、定义一个单独的函数块来实现复杂的操作:

当你的功能很复杂,变量很多你又不愿意增加一个函数的时候,使用do{}while(0);,将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。

时间: 2015-01-17

浅谈c++中的while(cin)问题

xp系统中利用dev-cpp进行编程,语句while(cin>>str),str是个string类型,在一行中输入几个string,末位加个ctrl+z,输入没有结束,除非出入换行后,再输入ctrl+z才能跳出输入.一直不明白未什么,解释请看下. 输入缓冲是行缓冲.当从键盘上输入一串字符并按回车后,这些字符会首先被送到输入缓冲区中存储.每当按下回车键后,cin.get()   就会检测输入缓冲区中是否有了可读的数据.cin.get()   还会对键盘上是否有作为流结束标志的   Ctrl+Z 

基于C++ cin、cin.get()、cin.getline()、getline()、gets()函数的使用详解

1.cin 2.cin.get() 3.cin.getline() 4.getline() 5.gets() 6.getchar() 附:cin.ignore();  cin.get()//跳过一个字符,例如不想要的回车,空格等字符 1.cin>>          用法1:最基本,也是最常用的用法,输入一个数字: #include <iostream> using namespace std; main () {    int a,b;    cin>>a>&g

基于C++ bitset常用函数及运算符(详解)

C++ bitset--高端压位卡常题必备STL ------------------------------------------------------------ 以下内容翻译自cplusplus.com,极大地锻炼了我的英语能力. bitset存储二进制数位. bitset就像一个bool类型的数组一样,但是有空间优化--bitset中的一个元素一般只占1 bit,相当于一个char元素所占空间的八分之一. bitset中的每个元素都能单独被访问,例如对于一个叫做foo的bitset,

PHP基于Closure类创建匿名函数的方法详解

本文实例讲述了PHP基于Closure类创建匿名函数的方法.分享给大家供大家参考,具体如下: Closure 类 用于代表匿名函数的类. 匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象.在过去,这个类被认为是一个实现细节,但现在可以依赖它做一些事情.自 PHP 5.4 起,这个类带有一些方法,允许在匿名函数创建后对其进行更多的控制. 这个类不能实例化,里面主要有两个方法,都用来复制闭包,一个静态一个动态,下面分别详细讲解下这两个不好理解的方法. Closure::bind publ

基于JavaScript实现继承机制之构造函数方法对象冒充的使用详解

继承的方式 ECMAScript 实现继承的方式不止一种.这是因为 JavaScript 中的继承机制并不是明确规定的,而是通过模仿实现的.这意味着所有的继承细节并非完全由解释程序处理.作为开发者,你有权决定最适用的继承方式.最原始的继承实现方式就是对象冒充,下面着重介绍该方法. 对象冒充 对象冒充实现继承的核心其实依赖于在函数环境中使用 this 关键字.其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Class

基于Android中Webview使用自定义的javascript进行回调的问题详解

先说为什么需要讨论这个问题. 现在很多的手机应用,都可能会直接嵌入一个web页面.这样做的好处:一个是功能更新方便,维护起来容易,只需要维护服务器的页面即可,不需要更新客户端:另一个是功能通用,不仅android可以用,ios也可以用,symbian也可以直接用. 那为什么现在很多手机应用并不做成web方式的呢?原因很多.一个是现阶段web方式展现能力相对较弱,如果对于应用的美观程度要求比较高,就无法使用web方式:一个是web方式速度相对较慢,用户体验会受一些影响:一个是现阶段流量还是相对宝贵

基于C/C++时间函数的使用详解

C/C++对时间的操作也有许多值得大家注意的地方.最近,在技术群中有很多网友也多次问到过C++语言中对时间的操作.获取和显示等等的问题.下面,在这篇文章中,笔者将主要介绍在C/C++中时间和日期的使用方法.通过学习许多C/C++库,你可以有很多操作.使用时间的方法.但在这之前你需要了解一些"时间"和"日期"的概念,主要有以下几个:Coordinated Universal Time(UTC):协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间(Gre

基于mysql时间处理函数的应用详解

DAYOFWEEK(date) 返回日期date的星期索引(1=星期天,2=星期一, --7=星期六).这些索引值对应于ODBC标准.  mysql> select DAYOFWEEK('1998-02-03');  -> 3 WEEKDAY(date)  返回date的星期索引(0=星期一,1=星期二, --6= 星期天).  mysql> select WEEKDAY('1997-10-04 22:23:00');  -> 5  mysql> select WEEKDAY

基于php iconv函数的使用详解

unix下安装PHP的module,需要重新编译PHP,Windows下安装模板,只需将php.ini里的配置打开相应的dll就可,例如,需要加入gb库的支持,需要如下设置:extension_dir = "C:/ipaddr/php/extensions/"(注意,建议写全地址,并且后面加上/,很多时候是因为这里设置不对,才导致无法加载其它模块的dll的)再打开extension=php_gd2.dll但如果是安装iconv.dll,按上面方法,打开php_iconv.dll后,还是

基于empty函数的判断详解

$a = '';echo '1.-----------'.($a == '').'<br>';echo '2.-----------'.($a == null).'<br>';echo '3.-----------'.($a == false).'<br>';echo '4.-----------'.($a == '0').'<br>';echo '5.-----------'.($a == 0).'<br>';$a = null;echo '6

PHP中基于ts与nts版本- vc6和vc9编译版本的区别详解

VC6:legacy Visual Studio 6 compiler,就是使用这个编译器编译的.VC9:Visual Studio 2008 compiler,就是用微软的VS编辑器编译的.由于apache.org只提供VC6的版本,所以使用原版apache时只能使用VC6.(www.apachelounge.com上有apache VC9的版本提供,应该可以和PHP VC9配合,没用过)TS:Thread Safe 线程安全, 执行时会进行线程(Thread)安全检查,以防止有新要求就启动新