<ol>

<li>函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以传参时要格外小心。</li>

<li>调用真正函数的代码和调用函数式宏定义的代码编译生成的指令不同。如果是真正的函数,那么它的函数体要编译生成指令,代码中出现的每次调用也要编译生成传参指令和call指令。而如果是函数式宏定义,这个宏定义本身倒不必编译生成指令,但是代码中出现的每次调用编译生成的指令都相当于一个函数体,而不是简单的几条传参指令和call指令。所以,使用函数式宏定义编译生成的目标文件会比较大。</li>

<li>定义这种宏要格外小心,要注意括号的使用,和宏展开对运算的优先级的影响。</li>

<li>调用函数时先求实参表达式的值再传给形参,如果实参表达式有Side Effect,那么这些Side Effect只发生一次。但如果是宏定义,则可能存在多次Side Effect。</li>

<li>即使实参没有Side Effect,使用函数式宏定义也往往会导致较低的代码执行效率。</li>
</ol>

尽管函数式宏定义和真正的函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作,因此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现。例如C标准库的很多函数都提供两种实现,一种是真正的函数实现,一种是宏定义实现。

例:
<pre lang="c" escaped="true" line="1">
#define device_init_wakeup(dev,val) \
do { \
device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val); \
} while(0)

</pre>

为什么要用do { ... } while(0)括起来呢?不括起来会有什么问题呢?

<pre lang="c" escaped="true" line="1">
#define device_init_wakeup(dev,val) \
device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val);

if (n > 0)
device_init_wakeup(d, v);

</pre>

这样宏展开之后,函数体的第二条语句不在if条件中。那么简单地用{ ... }括起来组成一个语句块不行吗?

<pre lang="c" escaped="true" line="1">
#define device_init_wakeup(dev,val) \
{ device_can_wakeup(dev) = !!(val); \
device_set_wakeup_enable(dev,val); }

if (n > 0)
device_init_wakeup(d, v);
else
continue;
</pre>

问题出在device_init_wakeup(d, v);末尾的;号,如果不允许写这个;号,看起来不像个函数调用,可如果写了这个;号,宏展开之后就有语法错误,if语句被这个;号结束掉了,没法跟else配对。因此,do { ... } while(0)是一种比较好的解决办法。

<blockquote>中间的device_can_wakeup(dev) = !!(val); 为什么要取反两次呢?</blockquote>

这样做的目的使结果只有两个值“0”和“1”,也就是bool结果,在这里相当于val为0时取0,非0时取1了。
假设一个设备有多个状态,其中val = 0代表没有启用,1,2,3,4分别代表设备启用是处于不同的状态下。那么我就想记录一下设备是否启用了,就可以这样使用:
status = !!(val)
status为0代表未启用,1代表启用了。

#define XXX do{...}while(0)的更多相关文章

  1. #define xxx do{...} while(0) 宏定义

    linux内核和其他一些开源的代码中,经常会遇到这样的代码: do{ ... }while(0) 这样的代码一看就不是一个循环,do..while表面上在这里一点意义都没有,那么为什么要这么用呢? 实 ...

  2. #define XXX do{ XXX } while(0) 为什么使用

    #define XXX do{ XXX } while(0) 为什么使用 时常会遇到一个非常"奇怪的宏定义", rt.(欧西巴...思考不够深刻啊, 皮鞭, 啪啪啪) 近期又遇到这 ...

  3. #define SIG_DFL ((void(*)(int))0)

    在linux内的信号处理函数中,有#define SIG_DFL ((void(*)(int))0)和#define SIG_IGN ((void(*)(int))1)两个宏定义.要理解这两个宏定义, ...

  4. vs2010编译zapline-zapline.systemoptimization 注释工程中的//#define abs(value) (value >= 0 ? value : -(value))即可

    vs2010编译zapline-zapline.systemoptimization-8428e72c88e0.zip出错 1>d:\program files (x86)\microsoft ...

  5. #define x do{......} while(0)的用处

    比如定义宏,#define FREE1(p) if (p) free (p)然后这样调用:if (expression)FREE1(p);elseprintf(“expression was fals ...

  6. C语言-define 与do{}while(0)

    问题引出: 我们都知道宏定义#define只是简单替换,所以遇到复杂的带参数宏,必须很小心的为需要的参数加上括号“()”:同样碰到复杂的多条语句替代,虽然加{}可以将其封装成一个整体,但同时又有另一个 ...

  7. 模拟退火法(吊打XXX)Bzoj3680

    3680: 吊打XXX Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special Judge Submit: 308  Solved: 94 [Subm ...

  8. #define用法集锦

    Definition: The #define Directive You can use the #define directive to give a meaningful name to a c ...

  9. c语言宏定义#define的理解与资料整理

    1. 利用define来定义 数值宏常量 #define 宏定义是个演技非常高超的替身演员,但也会经常耍大牌的,所以我们用它要慎之又慎.它可以出现在代码的任何地方,从本行宏定义开始,以后的代码就就都认 ...

随机推荐

  1. sql server 函数

    1.Fun_Split 分割字符串,形成返回表 CREATE FUNCTION [dbo].[Fun_Split] ( @Items VARCHAR(MAX), @SplitStr VARCHAR(M ...

  2. 很不错的文章---【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群

    [导读]徐汉彬曾在阿里巴巴和腾讯从事4年多的技术研发工作,负责过日请求量过亿的Web系统升级与重构,目前在小满科技创业,从事SaaS服务技术建设. 大规模流量的网站架构,从来都是慢慢“成长”而来.而这 ...

  3. Codeforces Round #336 (Div. 2)C. Chain Reaction DP

    C. Chain Reaction   There are n beacons located at distinct positions on a number line. The i-th bea ...

  4. 15条规则解析JavaScript对象布局(__proto__、prototype、constructor)

    大家都说JavaScript的属性多,记不过来,各种结构复杂不易了解.确实JS是一门入门快提高难的语言,但是也有其他办法可以辅助记忆.下面就来讨论一下JS的一大难点-对象布局,究竟设计JS这门语言的人 ...

  5. 多线程包:java.util.concurrent,

    Java1.5提供了一个非常高效实用的多线程包:java.util.concurrent,

  6. Ubuntu LAMP搭建

    为了数据库课程设计,只好自己搭一个数据库系统,采用LAMP方式. 一.安装 1.安装Apache sudo apt-get install apache2 Apache在安装期间会新建一个目录:/va ...

  7. iOS 深拷贝和浅拷贝

    注意,本文只说结论不说过程 1. 对于系统的非容器类对象,我们可以认为,如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝).如果是对可变对象复制,都是深 ...

  8. iOS:CALayer核心动画层上绘图

    在CALayer上绘图: •要在CALayer上绘图,有两种方法: 1.创建一个CALayer的子类,然后覆盖drawInContext:方法,可以使用Quartz2D API在其中进行绘图 2.设置 ...

  9. 怎样做出优秀的扁平化设计风格 PPT 或 Keynote 幻灯片演示文稿?(装)

    不知道你有没有想过,为什么很人多的扁平化 PPT 是这个样子: 或者是这样: 然而,还有一小撮人的扁平化 PPT 却拥有那么高颜值: 为什么会产生这么大的差距呢?丑逼 PPT 应该如何逆袭成为帅逼呢? ...

  10. AppDomain 应用程序域

    应用程序域 一.什么是应用程序域? 应用程序域 (application domain) (AppDomain) 一种边界,它由公共语言运行库围绕同一应用程序范围内创建的对象建立(即,从应用程序入口点 ...