大家都知道,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);
DELETE_POINTER(p);

2.避免if  else  不匹配;
举例说明如下:

#define PRINT_STRING(isDoc)   \if(isDoc)                 \printDoc();           

如果调用的时候如下面这样:

bool isReady = false;bool isDoc = true;if(isReady)PRINT_STRING(isDoc);elsedoOtherThing();

则此代码相当于

bool isReady = false;bool isDoc = true;if(isReady)if(isDoc)                printDoc();  elsedoOtherThing();

这显然与我们的本意不符。

还有其它的一些用法 , 有人总结的很清楚了,这是不在累赘。

原文:http://www.spongeliu.com/415.html

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(); \
})

http://www.spongeliu.com/   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);,将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。

其它相关参考博文:
http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html

http://blog.csdn.net/chenhu_doc/article/details/856468

欢迎加入"C/C++梦之队" 学习群:226157456

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

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

    有篇文章解释很清楚:do {...} while (0) in macros 然后,一位女程序媛把它翻译了一遍,排版清晰,想省力的看这边.do {...} while (0) 在宏定义中的作用

  2. 巧用c语言宏定义实现自动注释调试代码,C语言宏定义中的特殊用法

    C宏定义中的特殊用法 在分析一些C源码时,经常会遇到各种宏定义操作,本文即总结一下C语言宏定义中常见的预定义宏.调试宏:宏的条件编译用法及特殊的宏关键字用法. #undef 限定宏的作用域 一般来讲宏 ...

  3. 宏定义中#和##的用法

    转载:https://blog.csdn.net/baidu_33850454/article/details/79363033 1. 前言 使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起 ...

  4. #与##在宏定义中的--宏展开

    #与##在宏定义中的--宏展开 #include <stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int m ...

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

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

  6. C宏定义中的##,#,#@用法介绍

    关于字符串化和符号粘贴 2011-08-02 16:52:46.0      来源:博客园         作者:佚名 关键词:  字符串     C++   在C语言开发中,宏定义是一个非常有用的工 ...

  7. (转载) min()的宏定义中的(void) (_x == _y)的含义

    Original Address:http://www.crifan.com/2010/08/13/order_min__macro_definition_void_amp__x__amp__y_th ...

  8. ANSI C and Microsoft C++中常用的预定义宏以及 宏定义中 # 和 ## 的区别

    ANSI C and Microsoft C++中常用的预定义宏以及 宏定义中 # 和 ## 的区别 第一部分,常见的预定义宏 第二部分,# 和 ## 再宏定义中的使用说明 第三部分,类似 #prag ...

  9. c语言长度宏定义运算符,C语言在宏定义中使用语句表达式和预处理器运算符

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

最新文章

  1. 分布式任务分发框架Gearman测试、性能监控、队列持久化【python 实例】
  2. 安装windows时loading files结束就重启_Boot Camp安装windows 10
  3. python 残差图_python 残差图
  4. 不属于前后端分离的Vue+Django的例子
  5. caffe生成voc格式lmdb
  6. ASM 判定一个类,实现了指定接口
  7. Pmwiki基本编辑功能
  8. 李航《统计学习方法》-----支持向量机
  9. 2015-2020年各类国际会议与期刊基于图像的三维对象重建论文综述(7)——Datasets
  10. 元数据--MySQL获取元数据的方法
  11. 当有多个设备online时,命令行窗口通过adb连接指定设备方法
  12. poatman32位下载_Postman.dll下载|Postman.dll下载官方版【32位|64位】-太平洋下载中心...
  13. 华为交换机学习指南基于策略划分VLAN
  14. gedit c语言,让gedit 成为强大的C语言IDE
  15. android系统级浮层,android 新手引导浮层的实现
  16. python读取多张图片_Python批量导出多个PPT/PPTX文件中每个幻灯片为独立JPG图片
  17. 教你如何把软件转移到另一台电脑?
  18. 【News】华为海思AI视频监控芯片出货量超5亿,背后竟然离不开这家公司?
  19. 前端项目没数据?教你抓取各大网站api
  20. Java中resualtset,Java SafeEncoder類代碼示例

热门文章

  1. CComboBox控件详解
  2. (14) 2019运输科技领域最新SCIE期刊影响因子
  3. Android之进阶总结篇
  4. java excel api 下载文件_java excel api实现输出EXCEL文件下载
  5. 高斯分布+柯西-洛伦兹分布+三种光谱线型函数(洛伦兹线型函数+多普勒[高斯]线型函数+vogit 线型函数)
  6. 恒为linux笔试题,期终模拟测试一
  7. 如何找到靠谱的人才?
  8. 蓝桥冲刺31天之316
  9. setup中使用ref
  10. 「构建企业级推荐系统系列」构建优质的推荐系统服务