为什么在宏中使用看似毫无意义的 do-while 和 if-else 语句?
在许多 C/C++ 宏中,我看到宏的代码被包裹在一个看似毫无意义的 do while
循环中.以下是示例.
In many C/C++ macros I'm seeing the code of the macro wrapped in what seems like a meaningless do while
loop. Here are examples.
#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else
我看不到 while
正在做什么.为什么不直接写这个呢?
I can't see what the do while
is doing. Why not just write this without it?
#define FOO(X) f(X); g(X)
推荐答案
do ... while
和 if ... else
可以做到一个宏后面的分号总是意味着同样的事情.比方说你有类似你的第二个宏的东西.
The do ... while
and if ... else
are there to make it so that a
semicolon after your macro always means the same thing. Let's say you
had something like your second macro.
#define BAR(X) f(x); g(x)
现在,如果您要在 if ... else
语句中使用 BAR(X);
,其中 if 语句的主体未包含在大括号中,你会得到一个糟糕的惊喜.
Now if you were to use BAR(X);
in an if ... else
statement, where the bodies of the if statement were not wrapped in curly brackets, you'd get a bad surprise.
if (corge)
BAR(corge);
else
gralt();
上面的代码会展开成
if (corge)
f(corge); g(corge);
else
gralt();
这在语法上是不正确的,因为 else 不再与 if 相关联.在宏中用大括号括起来并没有帮助,因为大括号后面的分号在语法上是不正确的.
which is syntactically incorrect, as the else is no longer associated with the if. It doesn't help to wrap things in curly braces within the macro, because a semicolon after the braces is syntactically incorrect.
if (corge)
{f(corge); g(corge);};
else
gralt();
有两种方法可以解决此问题.第一种是在宏中使用逗号对语句进行排序,而不会剥夺它像表达式一样的能力.
There are two ways of fixing the problem. The first is to use a comma to sequence statements within the macro without robbing it of its ability to act like an expression.
#define BAR(X) f(X), g(X)
上面版本的bar BAR
把上面的代码展开成下面的代码,语法正确.
The above version of bar BAR
expands the above code into what follows, which is syntactically correct.
if (corge)
f(corge), g(corge);
else
gralt();
如果你有一个更复杂的代码体而不是 f(X)
需要放在它自己的块中,例如声明局部变量,这不起作用.在最一般的情况下,解决方案是使用像 do ... while
这样的东西来使宏成为一个使用分号而不会混淆的单个语句.
This doesn't work if instead of f(X)
you have a more complicated body of code that needs to go in its own block, say for example to declare local variables. In the most general case the solution is to use something like do ... while
to cause the macro to be a single statement that takes a semicolon without confusion.
#define BAR(X) do {
int i = f(X);
if (i > 4) g(i);
} while (0)
你不必使用 do ... while
,你也可以用 if ... else
做一些事情,虽然当 if... else
在 if ... else
内部展开,它会导致dangling else",这可能会使现有的 dangling else 问题更难找到,如下面的代码所示.
You don't have to use do ... while
, you could cook up something with if ... else
as well, although when if ... else
expands inside of an if ... else
it leads to a "dangling else", which could make an existing dangling else problem even harder to find, as in the following code.
if (corge)
if (1) { f(corge); g(corge); } else;
else
gralt();
关键是在悬挂分号错误的上下文中用完分号.当然,此时可以(并且可能应该)争辩说,将 BAR
声明为实际函数而不是宏会更好.
The point is to use up the semicolon in contexts where a dangling semicolon is erroneous. Of course, it could (and probably should) be argued at this point that it would be better to declare BAR
as an actual function, not a macro.
总而言之,do ... while
可以解决 C 预处理器的缺点.当那些 C 风格指南告诉你放弃 C 预处理器时,这就是他们担心的事情.
In summary, the do ... while
is there to work around the shortcomings of the C preprocessor. When those C style guides tell you to lay off the C preprocessor, this is the kind of thing they're worried about.
相关文章