do ... while (0) — 它有啥用? [复制]
Posted
技术标签:
【中文标题】do ... while (0) — 它有啥用? [复制]【英文标题】:do ... while (0) — what is it good for? [duplicate]do ... while (0) — 它有什么用? [复制] 【发布时间】:2010-09-20 10:07:20 【问题描述】:可能重复:Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?
我已经看到这种表达方式 10 多年了。我一直在想它有什么好处。由于我主要在#defines 中看到它,因此我认为它适用于内部范围变量声明和使用中断(而不是 goto)。
它对其他东西有好处吗?你会用吗?
【问题讨论】:
看看this question。 实际上,它不是重复的,因为链接的 q/a 没有具体定义。比较两个答案很容易说明它不是重复的。 参见 Redis 第 53 行的“decrement_used_memory” [link]github.com/antirez/redis-tools/blob/master/zmalloc.c 重复的问题是标记为可能重复的问题(帖子的第一行),而不是 Federico A. Ramponi 给出的问题。 此do ... while ((void)0, 0)
的其他变体用于消除有关“恒定条件”的编译器警告。
【参考方案1】:
它是 C 中唯一可以用于#define
多语句操作、在其后放置分号并仍可在if
语句中使用的构造。一个例子可能会有所帮助:
#define FOO(x) foo(x); bar(x)
if (condition)
FOO(x);
else // syntax error here
...;
即使使用大括号也无济于事:
#define FOO(x) foo(x); bar(x);
在if
语句中使用它需要省略分号,这是违反直觉的:
if (condition)
FOO(x)
else
...
如果你这样定义 FOO:
#define FOO(x) do foo(x); bar(x); while (0)
那么下面的语法是正确的:
if (condition)
FOO(x);
else
....
【讨论】:
不会#define FOO(x) if(true) foo(x); bar(x); else void(0)
也可以工作,即使它更丑陋?
@user10607:是的,如果你的宏扩展是一个表达式的列表,它就可以工作。但是,如果您想在扩展中包含 if
或 while
,则该技巧也不起作用。
我仍然认为这是丑陋的,草率的编码,如果编码正确,可以避免。这可以追溯到一些常识性的 C 规则,即始终在 IF 语句上使用 curly,即使在 IF 之后只有一个操作。这应该是标准做法,IMVHO...
@LarryF:问题是,如果您正在编写一个供他人使用的头文件,您无法选择其他人将如何使用您的宏。为了避免给您的用户带来意外的惊喜,您必须使用类似上述的技术来使您的宏的行为与任何其他 C 语句的行为一样。
@AaronFranke 因为 ...;实际上由两个语句组成,... 部分和它后面的另一个空语句。做 ... 而(0);不过,这是一种说法。当用户编写 FOO(a, b, c) 时,Larry/Marco 期望它(除非明确另有说明)会产生一个表达式,就像调用返回 void 的函数一样。你的方法实际上是草率的。您的用户的代码样式约定应该与您无关。【参考方案2】:
这是一种简化错误检查并避免深层嵌套 if 的方法。例如:
do
// do something
if (error)
break;
// do something else
if (error)
break;
// etc..
while (0);
【讨论】:
er,将其分解为一个方法,并使用提前返回。 或者...只使用 do while(0)。 或使用goto
。不,说真的,在我看来,if (error) goto error;
和接近尾声的 error: ...
似乎是完成同样事情的更简洁的版本。
@WChargin:您链接的文章中的“goto fail”代码也会因“break”而失败。有人只是在那里复制了一条线。这不是 goto 的错。
@NiccoloM。 “当然,goto 不是错误,这实际上使它更有趣”【参考方案3】:
它有助于将多个语句组合成一个语句,这样一个类似函数的宏实际上可以用作一个函数。假设你有:
#define FOO(n) foo(n);bar(n)
你做到了:
void foobar(int n)
if (n)
FOO(n);
然后这扩展为:
void foobar(int n)
if (n)
foo(n);bar(n);
请注意,第二次调用 bar(n)
不再是 if
语句的一部分。
将两者都包装到do while(0)
中,您也可以在if
语句中使用宏。
【讨论】:
非常明确的答案。 +1 明白了。 ;) 为什么不在它周围放大括号呢?#define FOO(n) foo(n);bar(n)
@endolith 因为#define FOO(n) foo(n);bar(n)
会扩展为foo(n);bar(n)
。注意bar(n)
后面没有分号。【参考方案4】:
有趣的是,请注意以下情况,其中 do while (0) 循环不会为您工作:
如果你想要一个返回值的类函数宏,那么你需要一个statement expression: (stmt; stmt;) 而不是 do while(0):
#include <stdio.h>
#define log_to_string1(str, fmt, arg...) \
do \
sprintf(str, "%s: " fmt, "myprog", ##arg); \
while (0)
#define log_to_string2(str, fmt, arg...) \
( \
sprintf(str, "%s: " fmt, "myprog", ##arg); \
)
int main()
char buf[1000];
int n = 0;
log_to_string1(buf, "%s\n", "No assignment, OK");
n += log_to_string1(buf + n, "%s\n", "NOT OK: gcc: error: expected expression before 'do'");
n += log_to_string2(buf + n, "%s\n", "This fixes it");
n += log_to_string2(buf + n, "%s\n", "Assignment worked!");
printf("%s", buf);
return 0;
【讨论】:
这是一个 GCC 扩展。不过,在 C++11 中,你可以用 lambda 做同样的事情。 TIL 语句表达式。整洁的。 lambda 看起来如何?我很好奇。 lambda 返回一个仿函数,本质上,您仍然必须调用它,然后从宏返回结果。 lambda 和调用都需要形成一个语句。你怎么做呢?运算符,?【参考方案5】:一般来说,do
/while
适用于任何类型的循环构造,其中必须执行循环至少一次。可以通过直接的while
甚至for
循环来模拟这种循环,但结果通常不太优雅。我承认这种模式的特定应用相当罕见,但它们确实存在。一个浮现在脑海的是一个基于菜单的控制台应用程序:
do
char c = read_input();
process_input(c);
while (c != 'Q');
【讨论】:
它在 C# 中也可用,它没有宏。我不确定为什么有人对这个回复投了反对票,但我认为它几乎是正确的答案,除了它忽略了 while 条件中明确的“0”。请各位,如果您对某人的回复投反对票,请发表评论。 我不是投反对票的人;然而,这个问题是非常具体的,答案是普遍正确的,但断章取义。 声明是正确的,但它与上下文无关。以上是关于do ... while (0) — 它有啥用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
Python中for或while块末尾的“else”有啥用? [复制]