#define XXX do{...}while(0)
<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)的更多相关文章
- #define xxx do{...} while(0) 宏定义
linux内核和其他一些开源的代码中,经常会遇到这样的代码: do{ ... }while(0) 这样的代码一看就不是一个循环,do..while表面上在这里一点意义都没有,那么为什么要这么用呢? 实 ...
- #define XXX do{ XXX } while(0) 为什么使用
#define XXX do{ XXX } while(0) 为什么使用 时常会遇到一个非常"奇怪的宏定义", rt.(欧西巴...思考不够深刻啊, 皮鞭, 啪啪啪) 近期又遇到这 ...
- #define SIG_DFL ((void(*)(int))0)
在linux内的信号处理函数中,有#define SIG_DFL ((void(*)(int))0)和#define SIG_IGN ((void(*)(int))1)两个宏定义.要理解这两个宏定义, ...
- vs2010编译zapline-zapline.systemoptimization 注释工程中的//#define abs(value) (value >= 0 ? value : -(value))即可
vs2010编译zapline-zapline.systemoptimization-8428e72c88e0.zip出错 1>d:\program files (x86)\microsoft ...
- #define x do{......} while(0)的用处
比如定义宏,#define FREE1(p) if (p) free (p)然后这样调用:if (expression)FREE1(p);elseprintf(“expression was fals ...
- C语言-define 与do{}while(0)
问题引出: 我们都知道宏定义#define只是简单替换,所以遇到复杂的带参数宏,必须很小心的为需要的参数加上括号“()”:同样碰到复杂的多条语句替代,虽然加{}可以将其封装成一个整体,但同时又有另一个 ...
- 模拟退火法(吊打XXX)Bzoj3680
3680: 吊打XXX Time Limit: 10 Sec Memory Limit: 128 MBSec Special Judge Submit: 308 Solved: 94 [Subm ...
- #define用法集锦
Definition: The #define Directive You can use the #define directive to give a meaningful name to a c ...
- c语言宏定义#define的理解与资料整理
1. 利用define来定义 数值宏常量 #define 宏定义是个演技非常高超的替身演员,但也会经常耍大牌的,所以我们用它要慎之又慎.它可以出现在代码的任何地方,从本行宏定义开始,以后的代码就就都认 ...
随机推荐
- root 授权
错误:The user specified as a definer ('root'@'%') does not exist 解决: grant all privileges on *.* to ro ...
- App接口设计
关于APP接口设计 http://blog.csdn.net/gebitan505/article/details/37924711/
- 2014多校第七场1005 || HDU 4939 Stupid Tower Defense (DP)
题目链接 题意 :长度n单位,从头走到尾,经过每个单位长度需要花费t秒,有三种塔: 红塔 :经过该塔所在单位时,每秒会受到x点伤害. 绿塔 : 经过该塔所在单位之后的每个单位长度时每秒都会经受y点伤害 ...
- 项目上线与LOG记录
如果项目上过线的话,那你一定知道Log是多么重要. 为什么说Log重要呢?因为上线项目不允许你调试,你只能通过Log来分析问题.这时打一手好Log的重要性绝不亚于写一手好代码.项目出问题时,你要能拿出 ...
- strncpy 和 strcpy的区别 (要抽时间重点看,未完待续)
strcpy的实现: //GNU-C中的实现(节选): */ char* strcpy(char *d, const char *s) { char *r=d; while((*d++=*s++)); ...
- 重温《js权威指南》 第7,8章
第七章 数组 数组是值的有序集合.js数组是无类型的,数组元素可以是任意类型,同一个数组中不同元素也可能有不同的类型.数组可以动态增长或缩减,创建时无须生命那个一个固定的大小并且数组大 ...
- Android核心分析之二十六Android GDI之SurfaceFlinger
Android GDI之SurfaceFlinger SurfaceFinger按英文翻译过来就是Surface投递者.SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构.Sufac ...
- JSX架构及注释
一.架构 二.注释 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset=&q ...
- Bootstrap的clearfix
1.div的内容太多会导致后面的div错位 <!DOCTYPE html> <html> <head> <title>自定义占满wgnu</tit ...
- 获取CentOS系统详情的九个uname命令实例
当你在控制台模式下,无法通过“鼠标右键 > 关于”获取操作系统的信息.这时,在Linux下,你可以使用uname命令,帮助你完成这些工作. Uname是unix name的缩写.在控制台中实际使 ...