do-while-zero 结构在宏定义中的应用

来源:互联网 发布:南京市软件定制开发 编辑:程序博客网 时间:2024/05/16 07:14

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 结构用于封装语句序列并确保其是正确的。

注意:在宏语句体的末尾必须省略分号。 






原创粉丝点击