如果你是一名C程序员,你肯定很熟悉宏,它们非常强大,如果正确使用可以让你的工作事半功倍。然而,如果你在定义宏时很随意没有认真检查,那么它们可能使你发狂,浪费N多时间。在很多的C程序中,你可能会看到许多看起来不是那么直接的较特殊的宏定义。下面就是一个例子:

#define __set_task_state(tsk, state_value)      \
do { (tsk)->state = (state_value); } while ()

在Linux内核和其它一些著名的C库中有许多使用do{...}while(0)的宏定义。这种宏的用途是什么?有什么好处?

Google的Robert Love(先前从事Linux内核开发)给我们解答如下:

  do{...}while(0)在C中是唯一的构造程序,让你定义的宏总是以相同的方式工作,这样不管怎么使用宏(尤其在没有用大括号包围调用宏的语句),宏后面的分号也是相同的效果。

这句话听起来可能有些拗口,其实用一句话概括就是:使用do{...}while(0)构造后的宏定义不会受到大括号、分号等的影响,总是会按你期望的方式调用运行。

例如:

#define foo(x) bar(x); baz(x)

然后你可能这样调用:

foo(wolf);

这将被宏扩展为:

bar(wolf); baz(wolf);

这的确是我们期望的正确输出。下面看看如果我们这样调用:

if (!feral)
foo(wolf);

那么扩展后可能就不是你所期望的结果。上面语句将扩展为:

if (!feral)
bar(wolf);
baz(wolf);

显而易见,这是错误的,也是大家经常易犯的错误之一。

几乎在所有的情况下,期望写多语句宏来达到正确的结果是不可能的。你不能让宏像函数一样行为——在没有do/while(0)的情况下。

如果我们使用do{...}while(0)来重新定义宏,即:

#define foo(x) do { bar(x); baz(x); } while (0)

现在,该语句功能上等价于前者,do能确保大括号里的逻辑能被执行,而while(0)能确保该逻辑只被执行一次,即与没有循环时一样。

对于上面的if语句,将会被扩展为:

if (!feral)
do { bar(wolf); baz(wolf); } while ();

从语义上讲,它与下面的语句是等价的:

if (!feral) {
bar(wolf);
baz(wolf);
}

这里你可能感到迷惑不解了,为什么不用大括号直接把宏包围起来呢?为什么非得使用do/while(0)逻辑呢?

例如,我们用大括号来定义宏如下:

#define foo(x)  { bar(x); baz(x); }

这对于上面举的if语句的确能被正确扩展,但是如果我们有下面的语句调用呢:

if (!feral)
foo(wolf);
else
bin(wolf);

宏扩展后将变成:

if (!feral) {
bar(wolf);
baz(wolf);
};
else
bin(wolf);

大家可以看出,这就有语法错误了。

总结:Linux和其它代码库里的宏都用do/while(0)来包围执行逻辑,因为它能确保宏的行为总是相同的,而不管在调用代码中使用了多少分号和大括号。

C do {...} while (0) 在宏定义中的作用的更多相关文章

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

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

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

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

  3. 宏定义中使用do{}while(0)的好处 (转载)

    宏定义中使用do{}while(0)的好处   #define MACRO_NAME(para) do{macro content}while(0)   的格式,总结了以下几个原因:   1,空的宏定 ...

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. STL之lambda表达式

    一.简介 我们可以向一个算法传递任何类别的可调用对象.对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的.即,如果e是一个可调用的表达式,则我们可以编写代码e(args),其中ar ...

  2. 在webBrowser1.Navigate(url)中设置Cookie的注意点

    [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)] public static exte ...

  3. UsageGrideReport++

      迁移时间:2017年5月20日11:42:02CreateTime--2016年9月29日15:46:15Author:Marydon版本Gride Report++6.0使用说明:参考链接:ht ...

  4. Java基础2-容器篇

    java基础2-容器篇 1.页首请关注 思维导航大纲 1.常用容器的类型层次结构 2.理解容器的常用思维大纲 a.空间 时间 concurrentModifyException 加载因子 3.常用类源 ...

  5. Scala之Object (apply) dycopy

    一.前言 前面学习了Scala的Methods,接着学习Scala中的Object 二.Object Object在Scala有两种含义,在Java中,其代表一个类的实例,而在Scala中,其还是一个 ...

  6. Android API之android.os.Parcelable

    android.os.Parcelable Interface for classes whose instances can be written to and restored from a Pa ...

  7. TP3.2之引入第三方类库文件和普通.php文件

    1.引入第三方类库 .class.php文件 1.1 类库有写namespace命名空间 namespace Org\Util; class Auth { } 保存到ThinkPHP/Library/ ...

  8. mysql sleep连接太多怎么办

    摘要:interactive_timeout和wait_timeout参数对sleep连接的影响 interactive_timeout 参数含义:服务器关闭交互式连接前等待活动的秒数.交互式客户端定 ...

  9. HDUOJ ------1398

    http://acm.hdu.edu.cn/showproblem.php?pid=1398 Square Coins Time Limit: 2000/1000 MS (Java/Others)   ...

  10. JS打开新窗口防止被浏览器阻止的方法[转]

    本文实例讲述了JS打开新窗口防止被浏览器阻止的方法.分享给大家供大家参考.具体分析如下: 用传统的window.open()方式打开新窗口,会被浏览器阻止,那么,我们如何才能让JS打开新窗口不被浏览器 ...