做{...}而(0) - 有什么好处?

gil*_*ilm 315 c loops

可能重复:
为什么在C/C++宏中有时会出现无意义的do/while和if/else语句?

我已经看了十多年了.我一直在努力思考它有什么好处.因为我主要在#defines中看到它,所以我认为它对内部范围变量声明和使用中断(而不是gotos)有好处.

对其他事情有好处吗?你用它吗?

Gre*_*ill 466

它是C中唯一可用于#defineif语句操作的结构,后面加一个分号,并且仍然在语句中使用.一个例子可能有帮助:

#define FOO(x) foo(x); bar(x)

if (condition)
    FOO(x);
else // syntax error here
    ...;
Run Code Online (Sandbox Code Playgroud)

即使使用大括号也无济于事:

#define FOO(x) { foo(x); bar(x); }
Run Code Online (Sandbox Code Playgroud)

if语句中使用它将要求您省略分号,这是违反直觉的:

if (condition)
    FOO(x)
else
    ...
Run Code Online (Sandbox Code Playgroud)

如果你这样定义FOO:

#define FOO(x) do { foo(x); bar(x); } while (0)
Run Code Online (Sandbox Code Playgroud)

那么以下语法是正确的:

if (condition)
    FOO(x);
else
    ....
Run Code Online (Sandbox Code Playgroud)

  • @LarryF:问题是,如果您正在编写供其他人使用的头文件,则无法选择其他人如何使用您的宏.为了避免对用户造成意外的意外,您必须使用上述技术使您的宏行为与任何其他C语句的行为一样. (45认同)
  • 如果编码正确的话,我仍然认为这是UGLY和SLOPPY编码,并且可以避免.这可以追溯到一些常识C规则,总是在你的IF语句中使用卷曲,即使在IF之后只有一个操作.这应该是标准做法,IMVHO ...... (13认同)
  • 不会`#define FOO(x)if(true){foo(x); 杆(X); } else void(0)`即使它更丑陋也可以工作? (5认同)
  • @ user10607:是的,如果你的宏扩展是*表达式*的列表,那么这是有效的.但是,如果你想在扩展中包含一个`if`或`while`,那么这个技巧也行不通. (4认同)
  • @AaronFranke 因为{...};实际上由两个语句组成,即 {...} 部分和其后的另一个空语句。做{...} while(0); 不过,这是一个声明。Larry/Marco 当用户编写 FOO(a, b, c) 时,他们期望它(除非另有明确意图)产生一个表达式,就像调用一个返回 void 的函数一样。你的做法实际上是马虎的做法。您的用户的代码风格约定不应该是您关心的问题。 (3认同)
  • 但是`#define FOO(x)foo(x),bar(x)`如何使用逗号代替分号.如果不这样做,这不会很好吗? (2认同)
  • @Adisak,我认为这会破坏你的例子:`FOO(x), bar();`,因为`,bar()`会被吸收到`void(0),bar()`中。即逗号运算符会应用在错误的位置,导致奇怪的行为。`do { ... } while(0)` 已经非常完善,做了它应该做的事情。最重要的是,当有经验的开发人员看到别人写的“do { ... } while(0)”时,他们可以准确地看到发生了什么。为什么要使用其他东西,即使它可能有效,这只会让经验丰富的开发人员担心? (2认同)
  • 这个答案多么令人惊讶 (2认同)
  • @Max好的,如果您仍然对自己的问题有疑问,请提出新问题,而不是使用评论.谢谢. (2认同)
  • 我不明白为什么支架不起作用。 (2认同)

Jer*_*nes 105

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

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);
Run Code Online (Sandbox Code Playgroud)

  • 或者使用`goto`.不,严重的说,`if(错误)转到错误;`和一个`错误:......`接近结尾似乎是一个更清洁的版本,对我来说完成同样的事情. (63认同)
  • 呃,将其纳入方法,并使用早期回报. (14认同)
  • 或者......只需使用do {} while(0). (13认同)
  • @WChargin:您链接的文章中的"goto fail"代码也会因"中断"而失败.有人刚刚在那里复制了一条线.这不是goto的错. (5认同)
  • @NiccoloM."被授予,goto不是错误,这实际上让它更有趣" (4认同)
  • 并没有让它变得“更有趣”,只是让它成为一个完全空洞的评论 (2认同)

Mar*_*wis 76

它有助于将多个语句分组到一个语句中,因此类似函数的宏实际上可以用作函数.假设你有

#define FOO(n)   foo(n);bar(n)
Run Code Online (Sandbox Code Playgroud)

你也是

void foobar(int n) {
  if (n)
     FOO(n);
}
Run Code Online (Sandbox Code Playgroud)

然后这扩大到

void foobar(int n) {
  if (n)
     foo(n);bar(n);
}
Run Code Online (Sandbox Code Playgroud)

请注意,第二个调用(bar(n))不再是if语句的一部分.

将两者都包装到do {} while(0)中,也可以在if语句中使用宏.

  • @endolith因为`#define FOO(n) {foo(n);bar(n)}`会扩展为`{foo(n);bar(n)}`。请注意,“bar(n)”后面没有分号。 (5认同)
  • 非常明确的答案.+1 (2认同)
  • 清楚地明白了。;) (2认同)

小智 18

有趣的是,要注意以下情况:在做{}而(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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这是GCC扩展.但是在C++ 11中,你可以用lambda做同样的事情. (4认同)

归档时间:

查看次数:

137207 次

最近记录:

6 年,2 月 前