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 结构用于封装语句序列并确保其是正确的。注意:在宏语句体的末尾必须省略分
号。