这操作太神奇了,完全没有想到还有这种妙用
这个表达式主要用在宏定义和条件分支两种情况:
一是宏定义的时候,可以避免由多个语句组成的宏,在没有{}
的控制语句下产生错误的语义,如:
#define func() funcA(); funcB()
...
if(cond) func();
就会导致funcA
和funcB
在cond == 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
版本缩减重复代码的优点。
当然,如果有人想if
套if
,就当我没说
参考:http://www.spongeliu.com/415.html
参考文章中还提到了定义空宏和代码块功能,感觉这两个有点牵强。。定义空宏的话直接用分号应该也行,代码块功能也和循环语句没有必然的联系。