do-while-zero 结构在宏定义中的应用
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 结构在宏定义中的应用的更多相关文章
- define宏定义中的#,##,@#及\符号
define宏定义中的#,##,@#及\符号 在#define中,标准只定义了#和##两种操作.#用来把参数转换成字符串,##则用来连接两个前后两个参数,把它们变成一个字符串. 1.# (string ...
- 宏定义中的##操作符和... and _ _VA_ARGS_ _
1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...
- 宏定义中使用do{}while(0)的好处 (转载)
宏定义中使用do{}while(0)的好处 #define MACRO_NAME(para) do{macro content}while(0) 的格式,总结了以下几个原因: 1,空的宏定 ...
- C语言在宏定义中使用语句表达式和预处理器运算符
语句表达式的亮点在于定义复杂功能的宏.使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞.下面以一个简单的最小值的宏为例子一步步说明. 1.灰常简单的么,使用条件运算符 ...
- #define宏定义中## #@ # \ 符号使用
C/C++ 宏命令的神奇用法. 先看下面三条语句: #define Conn(x,y) x##y#define ToChar(x) #@x#define ToString(x) ...
- C语言宏定义中的#和##的作用【转】
本文转载自:http://my.oschina.net/shelllife/blog/123202 在宏定义中#和##的作用是:前者将宏定义的变量转化为字符串:后者将其前后的两个宏定义中的两个变量无缝 ...
- C++宏定义中"#"与"##"的妙用
在C++开发当中经常用到宏的定义当中使用"#"或者"##",以下是对着两种符号使用方法的简单描述: define中的#就是把#后面的参数当做一个符号来使用,简单 ...
- C语言可变参数在宏定义中的应用
在C语言的标准库中,printf.scanf.sscanf.sprintf.sscanf这些标准库的输入输出函数,参数都是可变的.在调试程序时,我们可能希望定义一个参数可变的输出函数来记录日志,那么用 ...
- C在宏定义中使用的语言可变参数
于C标准库的语言,printf.scanf.sscanf.sprintf.sscanf入输出函数,參数都是可变的.在调试程序时.我们可能希望定义一个參数可变的输出函数来记录日志,那么用可变參数的宏是一 ...
随机推荐
- shader一些语义或术语的解释
1.unity内置的摄像机和屏幕参数: 2.unity中一些常用的包含文件: 3.unityCG.cginc中一些常用的结构体: 4.unityCG.cginc中一些常用的帮助函数: 5.从应用阶段传 ...
- ios 深入讲解iOS键盘一:控制键盘隐藏显示
在iOS的开发中,我们一般使用UITextField.UITextView处理文字输入等操作,大部分情况下我们只需要一两行代码去手动管理键盘的显示隐藏:让UITextField或UITextView成 ...
- vptr
#include <stdio.h> class Point3d { public: virtual ~Point3d(){} public: static Point3d origin; ...
- struts2一个实例中遇到的问题
今天实现了一个登录功能的Struts2小程序. 期间遇到了许多问题,记忆犹新的是 (1)新版本的tomcat9和eclipse Neon Release (4.6.0) 发生了冲突,启动服务器的时候老 ...
- 4 Values whose Sum is 0(二分)
4 Values whose Sum is 0 Time Limit: 15000MS Memory Limit: 228000K Total Submissions: 21370 Accep ...
- windowsphone8.1学习笔记之应用数据(四)
应用数据的存储格式常用的分为json和xml两种(其实我都想略过这个地方的,json我一直用的是json.net,而wp上操作xml的方式与其他相比也没太多变化). 先说说json数据存储,关于jso ...
- SpringMVC的第一个入门案例
用户提交一个请求,服务器端处理器在接收到这个请求后,给出一条欢迎信息,在页面中显示. 第一步:导入jar包 在原有Springjar包基础上添加2个jar包 spring-webmvc-4.2.0.R ...
- linux c编程:管道
2在前面介绍过,进程之间交换信息的唯一途径就是传送打开的文件.可以经由fork或者exec来传送.这一章将介绍新的进程共享方式 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都 ...
- Symfony 没有找到数据库驱动An exception occured in driver: could not find driver
如果一直报这个错误, 第一,你本地没有相关的数据库驱动(mysql:-->pdo_myql,postgresql-->pdo_pgsql等); 需要执行 php -m|grep -i pd ...
- Nodejs课堂笔记-第二课 package.json的作用
本文由Vikings(http://www.cnblogs.com/vikings-blog/) 原创,转载请标明.谢谢! 上节课,我们打造了一下IDE工具-web storm的显示界面.至少现在回到 ...