我已经看到这个表达式十多年了。我一直在思考它的用途。由于我主要在#define中看到它,我认为它适用于内部作用域变量声明和使用break(而不是goto)。
除此之外,它还有什么好处吗?您使用它吗?
在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
...
#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)
这个定义也能够工作吗? - Adisak这是一种简化错误检查并避免深度嵌套if语句的方法。例如:
do {
// do something
if (error) {
break;
}
// do something else
if (error) {
break;
}
// etc..
} while (0);
goto
。不,我是认真的,对我来说,if (error) goto error;
和在结尾处附带一个 error: ...
看起来更简洁,也能达到相同的效果。 - FireFly将多个语句组合成一个可以使用函数式宏的单个语句块有助于使其像函数一样使用。假设您有以下代码:
#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
语句中使用该宏。
#define FOO(n) {foo(n);bar(n)}
- endolith#define FOO(n) {foo(n);bar(n)}
会被展开成{foo(n);bar(n)}
。请注意,在bar(n)
后面没有分号。 - John有趣的是,在以下情况下,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;
}
do
/while
循环适用于任何需要至少执行一次循环的情况。虽然可以通过简单的 while
或者 for
循环来模拟这样的循环,但结果通常会略显不够优美。我承认,这种模式的具体应用相当罕见,但确实存在。其中一个我想到的例子是基于菜单的控制台应用程序:do {
char c = read_input();
process_input(c);
} while (c != 'Q');
do { ... } while ((void)0, 0)
用于消除编译器关于“常量条件”的警告。 - Amro