BUILD_BUG_ON与BUILD_BUG_ON_ZERO函数

在epoll的源码中,有一个很奇怪的宏(Linux的奇技淫巧)。

// 用于检查EPOLL_CLOEXEC与O_CLOEXEC常量的相等性
...
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
...

跟踪到宏定义可以看到,这个妖怪的真面目。

#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
...
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))

先观察BUILD_BUG_ON_ZERO(e)这个妖怪。假如EPOLL_CLOEXEC != O_CLOEXEC这个条件为真,那么这个宏定义可以扩展成如下:

(sizeof(struct { int:-!!(1); }))
// 而`!(1) = 0`,`!!(1) = 1` 再次扩展
(sizeof(struct { int:-1; }))

我们知道上述是一个结构体的定义式,而且定义了一个位域,但是这个位域为-1位,那么编译肯定是要报错的,所以如果为真,就会终止编译。这个错误检查在编译过程就能做到,而不需要把检查过程放到程序的运行过程中。BUILD_BUG_ON_ZERO(e)这个妖怪的实际目的就是检查条件e是否为真,如果为真,则编译会出错。
那么有了这个妖怪为什么还要BUILD_BUG_ON(condition)这个妖怪呢。可以看到BUILD_BUG_ON_ZERO(e)这个宏定义使用了sizeof运算符,这个运算符是会产生结果的,也就是说如果编译通过,这个宏定义会产生一个结果0,那么这个结果我们是不需要的,也是未使用的。所以BUILD_BUG_ON(condition)结果0的前面加了(void),这可以让某些编译器不产生未使用的警告,或者说有的时候我们是不需要0这个结果的,而有时候是需要的。

posted @ 2020-06-27 20:15  dn96  阅读(453)  评论(0编辑  收藏  举报