do while 语句在使用宏定义时是一个有用的技巧,说明如下:

假设有这样一个宏定义



#define macro(condition) /

if(condition) dosomething()



现在在程序中这样使用这个宏:



if(temp)

    macro(i);

else

    doanotherthing();



一切看起来很正常,但是仔细想想。这个宏会展开成:



if(temp)

    if(condition) dosomething();

else

    doanotherthing();



这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。

为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。





因为在C语言中对宏是不进行语法检查的,所以在替换时有可能产生一些隐蔽的错误,上面举出了一例,再举一例:



#define INTI_RECT_VALUE( a, b )\

    a = 0;\

    b = 0;



使用宏时,



for (index = 0; index < RECT_TOTAL_NUM; index++)

    INTI_RECT_VALUE( rect.a, rect.b );



此时,宏定义的语句并未正确的纳入for循环中,因此,在使用宏定义多条语句时,有必要加一些“封装”措施,比如我们可以这样定义,



#define INTI_RECT_VALUE( a, b )\

{\

    a = 0;\

    b = 0;\

}



但这样使用时也有一个问题,就是我们不能再宏定义语句后面自然的加一个分号,“{ } ;”的形式在语法上是错误的。而定义成do-while-zero 结构的话,就可以避免这个问题。

所以要写一个包含多条语句的宏的话,使用do-while-zero 结构是保证安全性的一个重要技巧。

在MISRA C-2004中,肯定了这种结构的必要性,以下是其对使用宏定义的规则:

规则19.4 (强制): C的宏只能扩展为用大括号括起来的初始化、常量、小括号括起来的

表达式、类型限定符、存储类标识符或do-while-zero 结构。 



这些是宏当中所有可允许使用的形式。存储类标识符和类型限定符包括诸如 extern 、static

和const这样的关键字。使用任何其他形式的#define 都可能导致非预期的行为,或者是非常难

懂的代码。 

特别的,宏不能用于定义语句或部分语句,除了do-while 结构。宏也不能重定义语言的

语法。宏的替换列表中的所有括号,不管哪种形式的 ()、{} 、[]  都应该成对出现。 

do-while-zero 结构(见下面的例子)是在宏语句体中唯一可接受的具有完整语句的形式。

do-while-zero 结构用于封装语句序列并确保其是正确的。注意:在宏语句体的末尾必须省略分

号。

do-while-zero 结构在宏定义中的应用的更多相关文章

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

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

  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. C语言在宏定义中使用语句表达式和预处理器运算符

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

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

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

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

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

  7. C++宏定义中"#"与"##"的妙用

    在C++开发当中经常用到宏的定义当中使用"#"或者"##",以下是对着两种符号使用方法的简单描述: define中的#就是把#后面的参数当做一个符号来使用,简单 ...

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

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

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

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

随机推荐

  1. js浅度克隆/深度克隆

    首先弄明白几个概念: 一. 具体数据类型分为两种:  1.原始数据类型  2.引用数据类型 原始数据类型存储的是对象的实际地址,包括: number.string.boolean.还有两个特殊的nul ...

  2. jqury 如何获取 kindeditor 中textarea 的值

    获取文本内容,可是的创建时怎么也不能获取,利用FF的firebug查看到自己所写的内容在一个iframe中,于是想从iframe中获取文本,想要用 $(“ifame”).html();获取内容,可是依 ...

  3. PHPstorm如何安装vue.js插件

    1.什么是PHPstorm? PhpStorm是一个轻量级且便捷的PHP IDE,其旨在提高用户效率,可深刻理解用户的编码,提供智能代码补全,快速导航以及即时错误检查.----来自百度百科 一句话:P ...

  4. AngularJS form $addControl 注冊控件control

    需求背景: 在form中使用编写的某component directive时.想通过form's name来对form中控件进行操作, 如使用$invalid等来ng-disabled btn. 解决 ...

  5. Java使用jmagick处理图片遇到的异常

    java通过ImageMagick处理图片遇到问题: 下面异常都是我一个一个遇到的: 异常1: Exception in thread "main" java.lang.Unsat ...

  6. 【译】StackOverflow——Java 中的 finally 代码块是否总会被执行?

    问题 有一个 try/catch 代码块,其中包含一个打印语句.finally代码块总会被调用么? 示例: try { something(); return success; } catch (Ex ...

  7. java.time.format.DateTimeFormatter

    Java的日期与时间 DateTimeFormatter类是Java 8中日期时间功能里,用于解析和格式化日期时间的类,位于java.time.format包下.   1.预定义的DateTimeFo ...

  8. 基于SQLAIchemy的Flask目录

    预先知识 flask的基本使用 快速搭建开发的目录,以后我们在用Flask开发项目的时候可以直接用这个目录,不需要再自己创建. flask-sqlalchemy flask-sqlalchemy相当于 ...

  9. genymotion device manager列表没有

    1.第一种原因:链接Genymotion官网的网络超时,无法加载Genymotion device列表,解决办法百度一下:配置Genymotion代理服务器,联网下载 2.第二种可能:检查是否正确安装 ...

  10. 培训笔记——ubuntu安装

    1.选择安装位置,如果是做双系统提前准备一个分区,如果覆盖安装就无所谓了2.下载iso镜像文件,制作启动盘,Windows或linux环境下分别有相应的软件可以制作启动光盘或U盘3.开始安装一 设置开 ...