为什么在宏中使用看起来毫无意义的do while和if else语句?

在许多C/C++宏中,我看到宏的代码被包装在一个似乎毫无意义的do-while循环中。这里有一些例子

定义FOO(X)do{f(X);g(X);}while(0)
#定义FOO(X)if(1){f(X);g(X);}else

我看不清在做什么。为什么不把它写下来呢

定义FOO(X)f(X);g(X)

do。。。而如果。。。否则会使
宏后面的分号总是表示相同的内容。比如说你
有点像你的第二个宏

定义条(X)f(X);g(x)

现在,如果要使用BAR(X)</如果。。。else语句,如果if语句的主体没有用花括号括起来,您会得到一个不好的惊喜

if(科奇)
巴(科奇);
其他的
gralt();

上述代码将扩展为

if(科奇)
f(科赫);g(科奇);
其他的
gralt();

这在语法上是不正确的,因为else不再与if关联。在宏中用大括号括起来是没有帮助的,因为大括号后面的分号在语法上是不正确的

if(科奇)
{f(corge);g(corge);};
其他的
gralt();

有两种方法可以解决这个问题。第一种方法是使用逗号对宏中的语句进行排序,而不会剥夺它作为表达式的能力

定义条(X)f(X),g(X)

上述版本的barbar将上述代码扩展为以下内容,语法正确

if(科奇)
f(科奇),g(科奇);
其他的
gralt();

如果有一个更复杂的代码体需要放在它自己的块中,例如声明局部变量,而不是f(X),那么这就不起作用了。在最普遍的情况下,解决方案是使用类似于do。。。而则使宏成为一条带分号的语句,而不会混淆

#定义条(X)do{\
int i=f(X)\
if(i&gt;4)g(i)\
}而(0)

您不必使用do。。。而,如果。。。else也一样,尽管当如果。。。else中展开,如果。。。else它会导致“悬挂else”,这可能会使现有的悬挂else问题更难找到,如下面的代码所示

if(科奇)
if(1){f(corge);g(corge);}else;
其他的
gralt();

关键是在悬空分号错误的上下文中使用分号。当然,在这一点上可以(也可能应该)认为,最好将BAR声明为实际函数,而不是宏

总之,do。。。而则是为了解决C预处理器的缺点。当那些C风格的指南告诉你停止C预处理器时,这正是他们担心的事情

发表评论