do { } while(0)和if (1) { }在宏中的区别

5

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

当需要在预处理器宏中执行多个语句时,通常会这样写:

#define X(a) do { f1(a); f2(a); } while(0)

因此,当此宏在像下面这样的表达式中使用时:

if (...)
    X(a);

不会出错。

问题是:我在哪里看到这样的表达式时,总是使用 do { ... } while(0);。有没有理由更喜欢这种方式而不是(在我看来更清晰的)if (1) { ... }?或者我的观察有误,它们同样受欢迎?


3
#define ever (;;) 的意思是定义一个常量 ever,其值为一个空语句 ;。然后可以使用 for ever { ... } 的形式来创建一个无限循环。 - Vinicius Kamakura
3
@KingsIndian提出的问题是为什么会有这样的陈述。而aland希望知道为什么其中一种更可取,我认为这是一个完全合理且不同的问题。请注意,本翻译保持原意,尽量通俗易懂,不包含任何解释或其他内容。 - penelope
1
@penelope 此外,如果我说dup,它不会立即变成dup。至少还需要4个人同意,而且如果社区认为所有5个关闭投票者都错了,它可以重新打开。 - P.P
5个回答

9

不,你没有错。

实际上有一个很好的原因:

#define my_code if (1) { ... }

if (1)
    my_code;

问题出在分号上!它不应该在那里...那看起来很奇怪,也不符合语言的精神。你可以选择有一个代码展开成两个连续的分号,或者一个看起来不像C语言的代码。

另一方面,do-while结构没有这个问题。


还有,正如其他人提到的,有一个else的问题:

if (1)
    my_code;
else { ... }

忽略分号的问题,else块现在属于错误的if。

7

if语句只有在存在else分支时才能像do/while语句一样安全。例如:

#define X(a) if(1) { f1(a); f2(a); } else{}

与以下同样安全:

#define X(a) do { f1(a); f2(a); } while(0)

为了让用户无法做到:

X(...) else ...;

一个区别在于使用 do/while 语句时需要在结尾跟上 ;,而 if/else 不需要。


1
你的解决方案仍然存在 ; 问题 - 你可以在代码中使用它作为 if (condition) X(a),不需要 ;,它不会强制用户结束语句,从而产生奇怪的未分号化代码。 - penelope
我认为if{}else{}更加通用,因为它可以带或不带;使用。个人而言,我不喜欢看起来像普通代码的宏,所以我更喜欢在宏后面不加; - Maxim Egorushkin
我接受Penelope的答案,因为它涵盖了问题中两种情况的区别。没有任何冒犯之意。 - aland
没关系,这是你的问题。 - Maxim Egorushkin
我认为如果 X(a) 使用 if(1){..}else{} 形式,那么 if (x) X(a); else doSomethingElse(); 将无法正常工作,但如果使用 do{...}while(0) 形式,则可以正常工作。 - supercat

5

请考虑以下内容:

if(foo)
    X(a);
else
    whatever();

这将会扩展为:
if(foo)
    if(1) { ... }
else
    whatever();

这是不好的,因为现在else属于错误的if语句。


https://dev59.com/0mTWa4cB1Zd3GeqPASnt#10720525 - Maxim Egorushkin

2

使用do...while循环结构可以在必要时使用break语句跳出循环。


打破什么?他想将 do-while 作为宏使用/不使用。 - penelope
4
如有必要,提前终止宏的执行。这在 Linux 源代码中广泛使用。 - James
对我来说,仅有一句话的解释看起来有点混乱和不太令人信服。你能添加一个代码片段来证明你的观点吗? - penelope

2
当你使用#define X(a) do { ... } while(0)这种形式时,它会强制你在语句X(1)的结尾加上;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接