小编典典

C multi-line macro: do/while(0) vs scope block

all

我见过一些包含在 do/while(0) 循环中的多行 C 宏,例如:

#define FOO \
  do { \
    do_stuff_here \
    do_more_stuff \
  } while (0)

与使用基本块相比,以这种方式编写代码有什么好处(如果有的话):

#define FOO \
  { \
    do_stuff_here \
    do_more_stuff \
  }

阅读 74

收藏
2022-06-23

共1个答案

小编典典

Andrey Tarasevich 提供了以下解释:

  1. 在 Google 网上论坛上
  2. bytes.com

[对格式进行了细微更改。在方括号[]] 中添加了括号注释。

使用“do/while”版本的整个想法是创建一个宏,它将扩展为常规语句,而不是复合语句。这样做是为了使函数式宏的使用与所有上下文中普通函数的使用一致。

考虑以下代码草图:

if (<condition>)
  foo(a);
else
  bar(a);

wherefoobar是普通函数。现在假设您想foo用上述性质的宏 [named CALL_FUNCS] 替换 function:

if (<condition>)
  CALL_FUNCS(a);
else
  bar(a);

现在,如果您的宏是按照第二种方法(只是{and
})定义的,则代码将不再编译,因为“真正的”分支if现在由复合语句表示。当你;在这个复合语句之后放一个时,你完成了整个if
语句,从而孤立了else分支(因此编译错误)。

解决此问题的一种方法是记住不要放在;宏“调用”之后:

if (<condition>)
  CALL_FUNCS(a)
else
  bar(a);

这将按预期编译和工作,但这并不统一。更优雅的解决方案是确保宏扩展为常规语句,而不是复合语句。实现此目的的一种方法是按如下方式定义宏:

#define CALL_FUNCS(x) \
do { \
  func1(x); \
  func2(x); \
  func3(x); \
} while (0)

现在这段代码:

if (<condition>)
  CALL_FUNCS(a);
else
  bar(a);

将编译没有任何问题。

CALL_FUNCS但是,请注意我的定义与您消息中的第一个版本之间的微小但重要的区别。我没有放 ;after } while (0)。将
a;放在该定义的末尾会立即破坏使用“do/while”的全部意义,并使该宏几乎等同于复合语句版本。

我不知道您在原始消息中引用的代码的作者为什么将其;放在while (0). 在这种形式中,两种变体是等价的。使用 ‘do/while’
版本背后的整个想法是不要将这个 final;包含在宏中(出于我上面解释的原因)。

2022-06-23