1. 程式人生 > 實用技巧 >BUILD_BUG_ON與BUILD_BUG_ON_ZERO函式

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這個結果的,而有時候是需要的。