do { ... } while (0) — 它有什么用?[复制]

do { ... } while (0) — what is it good for? [duplicate]

提问人:gilm 提问时间:11/3/2008 最后编辑:Henry Eckergilm 更新时间:6/4/2022 访问量:196132

问:

我看到这种表达已经超过10年了。我一直在思考它有什么好处。由于我主要在 #defines 中看到它,因此我认为它适用于内部范围变量声明和使用中断(而不是 gotos)。

它对其他东西有好处吗?你用它吗?

C 循环

评论

0赞 Federico A. Ramponi 11/3/2008
看看这个问题
13赞 Doomsday 5/18/2012
实际上,它不是重复的,因为链接的 q/a 不是特定定义的。很容易比较两个答案,以表明它不是重复的。
0赞 yet 12/10/2012
请参阅 Redis 第 53 行的“decrement_used_memory”[链接]github.com/antirez/redis-tools/blob/master/zmalloc.c
0赞 Étienne 5/2/2013
重复的问题标记为可能重复(帖子的第一行),而不是 Federico A. Ramponi 给出的问题。
2赞 Amro 1/1/2016
其他变体用于静音编译器关于“恒定条件”的警告。do { ... } while ((void)0, 0)

答:

138赞 Jere.Jones 11/3/2008 #1

这是一种简化错误检查和避免深嵌套 if 的方法。例如:

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);

评论

19赞 Dustin Getz 11/3/2008
呃,把它纳入一个方法,并使用早期回报。
17赞 nickf 11/3/2008
或。。。只需使用 do { } while(0)。
86赞 FireFly 4/24/2014
或者使用 .不,说真的,对我来说,接近结尾似乎是一个更干净的版本,可以完成同样的事情。gotoif (error) goto error;error: ...
6赞 Niccolo M. 6/15/2014
@WChargin:您链接的文章中的“goto fail”代码也会因“中断”而失败。有人只是在那里复制了一行。这不是Goto的错。
6赞 wchargin 6/16/2014
@NiccoloM。“当然,Goto不是错误,这实际上使它更有趣”
-8赞 Daniel Spiewak 11/3/2008 #2

一般来说,/ 适用于任何类型的循环构造,其中必须至少执行一次循环。可以通过直线甚至环路来模拟这种循环,但结果通常不那么优雅。我承认这种模式的具体应用相当罕见,但它们确实存在。我想到的是基于菜单的控制台应用程序:dowhilewhilefor

do {
    char c = read_input();

    process_input(c);
} while (c != 'Q');

评论

1赞 Jon Davis 11/3/2008
它也可以在 C# 中使用,它没有宏。我不确定为什么有人对这个回复投了反对票,但我认为它几乎是正确的答案,只是它忽略了 while 条件中明确的“0”。拜托,人们,如果你对某人的回复投反对票,请发表评论。
23赞 tzot 11/3/2008
我不是投反对票的人;然而,这个问题非常具体,答案一般是正确的,但断章取义。
2赞 Nadeem Khan 3/18/2019
陈述是正确的,但它是断章取义的。
113赞 Martin v. Löwis 11/3/2008 #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

评论

4赞 Anirudh Ramanathan 6/24/2015
非常明确的答案。+1
2赞 Whoami 7/6/2016
清楚地理解。;)
0赞 endolith 8/28/2021
为什么不在它周围戴上牙套呢?#define FOO(n) {foo(n);bar(n)}
4赞 John 10/19/2021
@endolith 因为会扩展到 .请注意,后面没有分号。#define FOO(n) {foo(n);bar(n)}{foo(n);bar(n)}bar(n)
616赞 Greg Hewgill 11/3/2008 #4

它是 C 语言中唯一可用于多语句操作的构造,在语句后加上分号,并且仍然可以在语句中使用。举个例子可能会有所帮助:#defineif

#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
    ....

评论

8赞 Adisak 8/8/2013
即使它更丑陋,也不会起作用吗?#define FOO(x) if(true){ foo(x); bar(x); } else void(0)
6赞 Greg Hewgill 11/4/2014
@user10607:是的,如果你的宏扩展是一个表达式列表,那就行了。但是,如果您想在扩展中包含 OR,那么该技巧也行不通。ifwhile
25赞 LarryF 7/17/2015
我仍然认为这是丑陋的,而且编码很邋遢,如果编码正确,可以避免。这又回到了一些常识性的 C 规则,即始终在 IF 语句中使用 curly's,即使您在 IF 之后只有一个操作。这应该是标准做法,IMVHO...
77赞 Greg Hewgill 7/17/2015
@LarryF:问题是,如果你要编写一个头文件供其他人使用,你就不能选择其他人如何使用你的宏。为了避免给用户带来意想不到的意外,您必须使用上述技术来使宏的行为与任何其他 C 语句的行为一样。
6赞 yonil 6/26/2020
@AaronFranke因为 {...};实际上由两个语句组成,{...} 部分和它后面的另一个空语句。做 {...} while(0);不过,这是一个声明。Larry/Marco 当用户编写 FOO(a, b, c) 时,他们希望它(除非另有明确意图)产生一个表达式,就像调用一个 void 返回函数一样。你的实际上是草率的方法。用户的代码样式约定不应是您关心的问题。
23赞 ubuntu-fanboy 1/8/2011 #5

有趣的是,请注意以下情况,其中 do {} while (0) 循环适合您:

如果你想要一个返回值的类似函数的宏,那么你将需要一个语句表达式:({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;
}

评论

8赞 celticminstrel 7/8/2015
这是 GCC 扩展。不过,在 C++11 中,你可以用 lambda 做同样的事情。
0赞 yonil 6/26/2020
TIL 语句表达式。整洁。lambda 看起来如何?我很好奇。lambda 返回一个函子,本质上,您仍然需要调用它,然后从宏返回结果。lambda 和 call 都需要形成一个语句。你是怎么做到的?算子?