C++-为什么有时候在C/C++宏里,有毫无意义的do/while和if/else块?
在很多C/C++宏里我发现有很多貌似无任何实际意义的do/while
或者if/else
代码块。比如:
#define FUNC(X) do { f(X); g(X); } while (0)
#define FUNC(X) if (1) { f(X); g(X); } else
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
一般情况下来说是为了防止宏的副作用,但是在不同的具体应用环境下可能有不同,可以把应用的环境贴出来看看
楼上说得很到位,其实cC++中的宏非常强大,可以实现很多高级功能,比如(#),(#@),(##)的使用。你看看MSDN上的#define讲解,我相信很多cC++程序员没去MSDN上查过#define。
宏定义最基本的需要把所有定义的东西用括号括起来吧?
do { f(X); g(X); } while (0)
这种写法确实是为了在某些情况下(调用函数出错,不能继续往下执行)
可以用break跳出来,这种做法叫做“统一出口”,类似于用goto,
目前来讲很多程序员或者公司都保留goto的这个作为统一出口的习惯用法。
而且非常有用。
你是在MFC下看到的代码吧,WINDOWS里经常使用这样的方式定义宏变量,这样可以有效的防治函数宏产生的副作用,同时,对于这样的宏,可以放心的调用。
#define FUNC(X) if (1) { f(X); g(X); } else
这个是为了保证语言执行的完整性,把这个执行过程展开一下你就明白了
比如:
if(true)
FUNC(x);
else
dosomething();
宏展开后成为如下代码:
if (true)
f(x); g(x);
else
dosomething();
这时执行完f(x)后,会自动跳出if(true),不执行g(x)了
所以执行到这里,就很容易明白了,这个宏这是为了保证可以连续执行
{ f(X); g(X); }
#define FUNC(X) do { f(X); g(X); } while (0)
这个有三方面的原因
1.单纯为了避免使用goto语句
2.如果用if代替do{..}while(0),因为if分支后有两个语句,else分支没有对应的if,编译错误,假设没有else, 第二个语句无论if测试是否通过,会永远执行。
以上二点,所以才会有了#define FUNC(X) do { f(X); g(X); } while (0)
一言以蔽之,就是#define FUNC(X) if (1) { f(X); g(X); } else有些情况下无法满足,所以用#define FUNC(X) do { f(X); g(X); } while (0)进行了扩展
这么做在我看来有如下几点考虑:
do/while 保证了这段代码只执行一次而无需关心do/while内部代码。
组织更高效的代码,在do/while包含中的代码,在编译的以后被作为一段完整的代码块。
还有就是保证语句的完整执行和语法错误,例如f(x)或者g(x)代码中有if分支语句,要是没有do-while把多条语句组织成一个代码块,则程序的运行结果就不正确,甚至不能编译。
通过内敛宏定义的这段代码,编译后省去了函数指针的调用,本身作为代码块节省了空
间。
对于非求值的宏,即代码块宏,用do {....}while(0)包围代码块,防止产生意外代码。
倘若使用{}包围代码,可能出现错误代码。比如:
#define boo()
{
....
}
在if语句中使用它,
if(x)
b00();
else
...
然而上述代码不能如预期的工作,展开后为
if(x)
{
....
}
;
else
...
可见多出了一个';',但使用do...while(0)不会出现此问题。
do...while(0)似乎多了一个不必要的循环,不过编译器优化时会识别。
do...while(0)另一用处在于避免goto语句的使用,比如:
do {
if(...)
break;/*不满足要求跳出*/
.....
if(...)
break;/*不满足要求跳出*/
.....
return;/*成功*/
}while(0)
..../*不满足后的处理*/
如果 f、g 是宏定义,那么这种定义多半是为了break跳出方便。另外补充一点对于
#define FUNC(X) if (1) { f(X); g(X); } else
这个定义,在使用时如果后面不加“;”有屏蔽下一条语句的影响,编译不会报错,所以这是一个比较危险的定义,慎用!。
如果f和g是函数,这俩例子确实没必要,顶多加个花括号吧;
如果f和g是宏,得看它们的内部处理了,比如对do,某些条件直接break就能跳过后续的代码;
通常见到的这种应用都不是这么简单的形式吧。
大家说得都挺好,宏只是简单的代码替换,加入这些是为了防止宏替换代码时对上下文产生副作用。对宏的使用看似简单,但真正用好挺不容易的。