BUILD_BUG_ON與BUILD_BUG_ON_ZERO函式
阿新 • • 發佈:2020-06-28
在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這個結果的,而有時候是需要的。