【问题标题】:Expand macro to no code: "do {} while (0)" or "while (0) {}"将宏扩展为无代码:“do {} while (0)”或“while (0) {}”
【发布时间】:2021-04-14 22:56:55
【问题描述】:

在我的代码中,我使用了一个宏,它应该生成一个日志条目,或者它不应该根据某些编译标志:

#ifdef SOMETHING                                                                
    #define LOG(a)     printf ("%s", a)                                         
#else                                                                           
    #define LOG(a)     while (0) {}                                             
#endif

这段代码运行良好,但是当我在互联网上查看时,我只看到有人在使用

#define LOG(a)     do {} while (0)

而不是

#define LOG(a)     while (0) {}

这两种方法是否等效?我应该更喜欢一个吗?

【问题讨论】:

  • 您为什么自己尝试并查看预处理后的输出?
  • 请记住,宏可以有 no 主体,即#define LOG(a) /* empty */ 是一个有效的宏。然后LOG("Some text"); 将扩展为/* empty */;,这是一个(空)语句。
  • @P__JsupportswomeninPoland :也许是因为在尝试时它可能会起作用。在非常特殊的情况下这是一个问题。
  • @Clifford do {} 而宏也适用于 very specific circumstances。人们不尝试,他们试图学习“简单”的方法。但它不起作用。我想知道:没有互联网和论坛,我怎么能学习编程?
  • @P__JsupportswomeninPoland 我明白你在说什么,有时答案是“试一试”,但同样,人们设计了有缺陷的实验,或者曲解了结果,或者经常得出笼统的结论未定义行为的结论。最后一个问题是“我应该更喜欢一个吗?”

标签: c while-loop macros do-while


【解决方案1】:

他们不一样

do {} while (0)

在语句末尾需要一个分号,而

while (0) {}

没有。

所以,当你写作时

LOG ("Some text\n");

第一个扩展为

do {} while (0);

这是一个单一的语句,但随着第二个它扩展为

while (0) {};

这是两个语句,while (0) {} 和 ;(这是一个有效的语句)。

当您将它与if 结合使用时,这很重要。我你写的是这样的:

if (1)
    LOG ("Condition is true\n");
else
    LOG ("Condition is false\n");

使用while (0) {} 方法,此扩展为

if (1)
    while (0) {};
else
    while (0) {};

相当于

if (1)
    while (0) {}
;
else
    while (0) {}
;

这不会编译,因为有一个 else 没有对应的 if。

因此,您应该更喜欢do {} while (0) 方法以获得更大的兼容性。此外,它更惯用。

【讨论】:

    【解决方案2】:

    do {} while (0) 和 while (0) {} 并非在所有上下文中都等价,例如,在后一种情况下,宏的用户自然会在末尾添加一个语法上不必要的分号,以便:

    if( error )
        LOG( "ERROR") ;
    else
    {
        ...
    }
    

    将扩展为:

    if( error )
        while(0){} ;
    else
    {
        ...
    }
    

    将else 与if 解除关联而失败,因为只有while(0){} 是有条件的。

    还应注意,两者在语法上都不等同于 表达式 printf ("%s", a),其类型为 int,更好的定义是:

    #ifdef SOMETHING                                                                
        #define LOG(a)     do{printf ("%s", a)} while(0)
    #else                                                                           
        #define LOG(a)     do{} while(0)
    #endif
    

    或

    #ifdef SOMETHING                                                                
        #define LOG(a)     printf ("%s", a)                                         
    #else                                                                           
        #define LOG(a)     0
    #endif
    

    这样LOG() 可以在语句 有效或表达式 有效的任何地方使用。 表达式 版本应该在可能使用表达式值的情况下使用。 表达式在独立语句中的扩展:

    0 ;
    

    是有效的无操作或空语句,但某些编译器或静态分析器可能会针对未使用的表达式发出警告。

    常见的无操作习语,例如:

    #define LOG(a)
    #define LOG(a) ((void)0)
    

    也可以考虑,但都缺乏与替代扩展的句法等价性,因为一个扩展在另一个有效的所有上下文中都是有效的。

    【讨论】:

      猜你喜欢
      • 2013-10-07
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      • 2015-04-30
      • 2010-11-07
      • 2016-08-22
      • 1970-01-01
      相关资源
      最近更新 更多