为什么 "do ... while (0)" 不能用简单的大括号代替?

Why "do ... while (0)" can't be replaced by simple curly brackets?

最近看到一段C代码,里面有一个宏,格式如下:

#define TOTO()              \
do {                        \
  do_something();           \
  do_another_something();   \
} while (0)

起初我想知道这里 do while (0) 的目的,但是 this answer 向我解释说:这是为了防止在 ifelse 没有大括号,像这样:

if (something)
    TOTO();
else
    do_something_else();

所以在这里,如果没有 do while (0) 语句,代码将扩展为:

if (something)
    do_something();
    do_another_something();
else
    do_something_else();

这在语法上是错误的,因为 else 不再直接跟随 if 范围。

但我认为通过在它自己的范围内声明宏也可以工作,而不需要在它周围使用 do while,所以我只用大括号测试了相同的代码。我的整个代码如下所示:

#include <stdio.h>

#define HELLO_WORLD()       \
{                           \
    printf("hello ");       \
    printf("world!\n");     \
}

int     main(int argc, char** argv)
{
    if (argc == 1)
        HELLO_WORLD();
    else
        fprintf(stderr, "nope\n");
    return 0;
}

但是 GCC 给我以下错误:

error: ‘else’ without a previous ‘if’

但是main函数的代码应该展开为:

if (argc == 1)
    {
        printf("hello ");
        printf("world!\n");
    }
else
    fprintf(stderr, "nope\n");
return 0;

哪个有效。

那么我在这里错过了什么?

宏后面的分号

宏扩展为:

if (argc == 1)
    {
        printf("hello ");       \
        printf("world!\n");     \
    };
else        // HERE, SYNTAX ERROR
    fprintf(stderr, "nope\n");
return 0;

由于 if 语句的主体和 else 子句之间不应有分号,因此这是一个语法错误。
OTOH,do-while 循环允许(并且需要)一个分号。


只需打印真实的预处理器输出即可轻松避免对编译器输出的误解。这可以通过使用

  • gcc 可执行文件的 -E 开关。来自 man 1 gcc:

-E Stop after the preprocessing stage; do not run the compiler proper. The output is in the form of preprocessed source code, which is sent to the standard output.

  • 直接以 cpp 可执行文件形式的预处理器。

感谢
- @dhke 用于更正发生错误的行。
- @kakeh 建议事先查看预处理器输出。

仔细查看您调用宏的方式。

你必须写

if (argc == 1)
    HELLO_WORLD() // NO SEMICOLON!!!!!!!
else
    fprintf(stderr, "nope\n");

这太可怕了,因为您永远无法用真正的函数替换 HELLO_WORLD 宏。