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这个结果的,而有时候是需要的。
如果文章对你有帮助,就点个赞吧!