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:是的,如果你的宏扩展是一个表达式的列表,它就可以工作。但是,如果您想在扩展中包含 ifwhile,则该技巧也不起作用。 我仍然认为这是丑陋的,草率的编码,如果编码正确,可以避免。这可以追溯到一些常识性的 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) — 它有啥用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

mongoose.model 中的第三个参数是啥?它有啥用

Python中for或while块末尾的“else”有啥用? [复制]

js中函数的prototype.constructor是指向函数本身,它有啥用

cookie 是啥,它有啥用

无头浏览器是啥?它有啥用?

请介绍下电脑中的环境变量,它有啥用呢?