395

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

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

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

4

5 回答 5

570

它是 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
    ....
于 2008-11-02T21:40:11.380 回答
124

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

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);
于 2008-11-02T21:38:44.207 回答
99

它有助于将多个语句组合成一个语句,这样一个类似函数的宏实际上可以用作一个函数。假设你有:

#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语句中使用宏。

于 2008-11-02T21:39:51.147 回答
20

有趣的是,请注意以下情况,其中 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;
}
于 2011-01-07T16:34:43.857 回答
-6

通常,do/适用于必须至少while执行一次循环的任何类型的循环构造。可以通过直线甚至循环来模拟这种循环,但结果通常不太优雅。我承认这种模式的特定应用相当罕见,但它们确实存在。一个浮现在脑海的是一个基于菜单的控制台应用程序:whilefor

do {
    char c = read_input();

    process_input(c);
} while (c != 'Q');
于 2008-11-02T21:39:02.940 回答