这操作太神奇了,完全没有想到还有这种妙用

这个表达式主要用在宏定义和条件分支两种情况:

一是宏定义的时候,可以避免由多个语句组成的宏,在没有{}的控制语句下产生错误的语义,如:

#define func() funcA(); funcB()

...

if(cond) func();

就会导致funcAfuncBcond == false的情况下产生错误的语义(原本计划是两条语句都不执行,而现在funcB必定执行)。

简单直观的解释:

if(cond)
    funcA();
funcB();

do {...} while(0)(注:这里while后面没有分号,目的是符合C/C++在语句后添加分号的风格,在调用宏的时候再加)则会被系统认为是一个单独的语句块,在面对分支结构的时候,就能够被正确地视作原子操作进行处理。

为什么不用{},而要用更复杂的do {...} while(0)呢?

直接上代码:

if(cond) { funcA(); funcB(); };

这个时候,原本在func末尾的分号成了累赘,反而会导致报错。这也是出于保持代码风格一致的考虑。

由于宏函数不存在返回值一说,而多语句的宏函数显然不会、也不应该产生返回值,所以无须担心把这类宏函数放到赋值语句中会怎么样——这和void类型的函数的行为是一致的。

二是简化条件汇集的情况。考虑这么一种情况:

if(condA) { funcA(); }
if(condB) { funcA(); funcB(); }
if(condC) { funcA(); funcB(); funcC(); }

如果真的在代码里这么写的话,想必是一座宏伟的代码金字塔了。随着条件增加,重复而又意义不大的函数体,会变得越来越长。于是有人考虑使用goto语句,合并重复的语句:

if(!condA) goto end;
funcA();

if(!condB) goto end;
funcB();

if(!condC) goto end();
funcC();

end:
    ...

但是对软件工程有所了解的人都会知道,在代码中使用goto是一种不清真的行为,会对代码的可读性和入口的单一性产生负面影响,影响后续的开发和维护工作。

继续观察,注意到上面例子中所有的情况下,只要到了某一条件不满足,就直接跳转到整个代码块的末尾。有什么语句可以跳过块内所有的剩余语句,立即跳出语句块?答案呼之欲出,break!于是,do {...} while(0); 作为能够使指令只执行一遍的循环结构,再次登场:

do {
    if(!condA) break;
    funcA();

    if(!condB) break;
    funcB();

    if(!condC) break;
    funcC();
} while(0);

这样既继承了原始代码不使用不规范函数的原则,又吸收了goto版本缩减重复代码的优点。
当然,如果有人想ifif,就当我没说

参考:http://www.spongeliu.com/415.html

参考文章中还提到了定义空宏和代码块功能,感觉这两个有点牵强。。定义空宏的话直接用分号应该也行,代码块功能也和循环语句没有必然的联系。

标签: none

添加新评论