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入输出函数,參数都是可变的.在调试程序时.我们可能希望定义一个參数可变的输出函数来记录日志,那么用可变參数的宏是一 ...
随机推荐
- PHP-Manual的学习----【安装与配置】
2017年6月27日17:59:16 安装与配置 安装前需要考虑的事项 Unix系统下的安装 Mac OS x系统下的安装 windows 系统下的安装 云计算平台上的安 ...
- POJ3468_A Simple Problem with Integers(线段树/成段更新)
解题报告 题意: 略 思路: 线段树成段更新,区间求和. #include <iostream> #include <cstring> #include <cstdio& ...
- 嵌套SQL语句訪问DB2中SQLCA的调用技巧
在IBM的关系型数据库产品DB2中,使用SQL Communication Area(SQLCA)将程序中嵌套的SQL语句执行情况返回给程序. 在程序中有针对性地对SQLCA实施调用,可对程序中各类S ...
- 九度OJ 1352:和为S的两个数字 (查找)
时间限制:2 秒 内存限制:32 兆 特殊判题:否 提交:3160 解决:833 题目描述: 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输 ...
- nginx + uWSGI 为 django 提供高并发
django 的并发能力真的是令人担忧,这里就使用 nginx + uwsgi 提供高并发 nginx 的并发能力超高,单台并发能力过万(这个也不是绝对),在纯静态的 web 服务中更是突出其优越的地 ...
- centos7下只需两个命令升级php版本
我的php5.4 升级到5.6 sudo yum clean allsudo yum install -y php56w Resolving Dependencies --> Running t ...
- VS2013 Pro版本密钥
Visual Studio Professional 2013 KEY(密钥): XDM3T-W3T3V-MGJWK-8BFVD-GVPKY
- Linux基础系列:常用命令(8)_shell script
一.什么是shell script 将OS命令堆积到可执行的文件里,由上至下的顺序执行文本里的OS命令 就是脚本了. 再加上些智能(条件/流控)控制,就变成了智能化脚本了 二.变量命名规则 以字母或下 ...
- JQuery 双击动态编辑
$(this).append(input); $("input#temp").focus(); $("inp ...
- Java InetAddress.getByAddress()的使用
import java.net.*; public class NetDemo { public static void main(String[] args) throws Exception{ S ...