#define foo() do { } while (0) 的目的是什么?

22

在浏览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引起的警告。我理解有时需要使用“无操作语句”来定义#。但是为什么要用这种特定的语法?具体来说,空大括号、false条件、do...while循环?有些语法细节我不太明白?
2个回答

23

这是一种常见的语法,用于通知编译器将宏视为语句而不是表达式语句 vs 表达式)。

在这种情况下,如果您尝试将 can_preempt_disable() 用作表达式,编译器会发出警告。这意味着我们强制进行了编译时检查,以确保can_preempt_disable()被用作语句。编译时检查通常是非常有用的。


2
(void)0可以达到防止表达式的值被使用的目标。 - M.M

17

相关文件中完整的内容请参考此处

#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*/ 
因此,第一部分是当您要求抢占保护时获得的代码,否则您会得到空的,无操作的循环。 我猜它们是这样编写的,出于通常的原因,即确保宏仍然是有效语句。在定义中不应该有终止的分号,因为这将在使用这些代码的代码中出现,例如这个函数,它以此开始:
int c_can_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
{
    can_preempt_disable(); 

    ...

因此,显然宏的使用方式与任何其他函数调用相同,并且分号就在宏被调用的地方。这非常正常。

更新2:将其定义为;会导致双分号,我认为这很丑陋。 一个空大括号对{}可能有效,但是由于它在这种情况下经常被使用,所以这个do/while结构更具惯用性。

更新3:如评论中指出的那样,一个空大括号对不起作用,因为在调用之后无法放置分号。啊。谢谢!


8
@SE 如果您进行了#define can_preempt_disable();,那么if(foo()) can_preempt_disable(); else bar();会变成语法错误的代码。如果您将其定义为空,即#define can_preempt_disable(),则while(foo()) can_preempt_disable();会在某些编译器(例如clang)中产生警告,并且您就可以在不应该的地方使用can_preempt_disable(),例如for(;can_preempt_disable();). - nos
2
"一个空的大括号 {} 应该可以工作" -- 不,它不能... if (x) can_preempt_disable(); else ... 无法编译。 do { } while(0) 的重要特点是它没有终止符号。 - Jim Balter
@JimBalter 谢谢,我知道但是在写的时候不知怎么忘了。已经编辑过了。 - unwind
1
然而,更好的想法是采用一种编码风格,在每个控制语句后始终使用{}。这样,您就不需要诉诸于晦涩的宏技巧。如果您有如此谨慎的编码风格,那么空的大括号一对就能完美地工作。因为当程序员粗心时,它会给出编译器错误。 - Lundin
@Lundin: 我完全同意。在我的上一家雇主那里,下一行没有大括号的if()语句是可以被解雇的。当我开始为现在的雇主工作时,我继承了代码中充斥着像if(debug) [newline] // printf("some debug message");这样的惊喜。不幸的是,我们经常被迫在一个不知道“总是使用大括号”的项目上工作。 - SF.
显示剩余2条评论

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