【问题标题】:Purpose of #define foo() do { } while (0)#define foo() do { } while (0) 的目的
【发布时间】:2014-12-01 19:10:16
【问题描述】:

在浏览LinCAN驱动源码时,我发现了一些令我困惑的宏。

#else /*CONFIG_PREEMPT*/
#define can_preempt_disable()      do { } while (0)
#define can_preempt_enable()       do { } while (0)
#endif /*CONFIG_PREEMPT*/

我明白了

的用处
do { 
  ...;
  if(condition) break;
  ... 
} while (0); 

使用break 作为throw 的一种。我半理解包装一系列函数,如

#define FOO() do { foo(); bar(); } while (0)

避免使用无括号 if 的警告。我知道有时#define 需要“无操作语句”。但为什么是这种特殊的类型?具体来说,空括号,错误条件,do...while?一些我不太理解的语法警告?

【问题讨论】:

    标签: c loops


    【解决方案1】:

    这是通知编译器宏应被视为语句而不是表达式statements vs expressions)的常用语法。

    在这种情况下,如果您尝试使用 can_preempt_disable() 作为表达式,编译器会提醒您。这意味着我们强制编译时检查 can_preempt_disable() 是否用作语句。编译时检查通常是可取的。

    【讨论】:

    • (void)0 会达到阻止表达式值被使用的目标。
    【解决方案2】:

    complete passage from the relevant file 是:

    #if !defined(CONFIG_PREEMPT_RT) && ( defined(CONFIG_PREEMPT) ||
        (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) )
    #define can_preempt_disable preempt_disable
    #define can_preempt_enable preempt_enable
    #else /*CONFIG_PREEMPT*/
    #define can_preempt_disable() do { } while (0)
    #define can_preempt_enable() do { } while (0)
    #endif /*CONFIG_PREEMPT*/ 
    

    因此,第一部分是您在请求抢占保护时获得的代码,否则您将获得空的、什么都不做的循环。

    我猜他们是因为通常的原因写成这样的,即确保宏仍然是一个有效的语句。

    定义中不应有终止分号,因为这将在使用这些分号的代码中,例如开头的this function

    int c_can_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
    {
        can_preempt_disable(); 
    
        ...
    

    因此,很明显,宏的使用与任何其他函数调用一样,分号就在调用宏的位置。这是很正常的。

    更新 2:将其定义为 ; 会导致使用双分号,这很难看,至少在我看来。 空大括号对 {} 可以工作我猜,但是这个 do/while 结构更加惯用,因为它经常用于此类情况。

    更新 3:正如评论中指出的那样,空括号对将不起作用,因为此后您不能在调用后放置分号。啊。谢谢!

    【讨论】:

    • 这不是我的意思——不是最后的分号,而是整个结构,#define can_preempt_disable() ;。分号是有效的空语句。 can_preempt_disable(); 将无害地评估为 ;;。或者,如果您担心无括号if...else,则可以使用纯空块{ }
    • @SE 如果你做了#define can_preempt_disable() ; 然后if(foo()) can_preempt_disable(); else bar(); -> if(foo());; else bar(); 这是一个语法错误。如果您将其定义为空 #define can_preempt_disable() ,则 while(foo()) can_preempt_disable(); -> while(foo()); 至少会在某些编译器(例如 clang)中产生警告,并且您可以在不应该使用的地方使用 can_preempt_disable() t,例如for(;can_preempt_disable();)
    • “我猜一个空的大括号对 {} 会起作用”——不,它不会……if (x) can_preempt_disable(); else ... 不会编译。 do { } while(0) 的重要特性是它不会被终止。
    • @JimBalter 谢谢,我知道,但不知何故记不起我是什么时候写的。已编辑。
    • 然而,一个更好的主意是采用一种编码风格,在每个控制语句之后总是使用 {}。那么你就不需要求助于晦涩的宏技巧了。如果您有如此谨慎的编码风格,那么空大括号对就可以了。因为在程序员马虎的时候会报编译错误。
    猜你喜欢
    • 1970-01-01
    • 2013-10-07
    • 1970-01-01
    • 2021-04-14
    • 2015-06-02
    • 2012-03-08
    • 2015-04-30
    • 1970-01-01
    • 2010-09-20
    相关资源
    最近更新 更多