在編譯時檢查sizeof

今天在寫文件系統時,想在編譯時檢查一些struct的大小,就發現了這篇blog,講得是內核裏面的BUILD_BUG_ON的實現,這個優雅精妙的實現讓我對內核開發者的敬佩之情真是油然而生啊!!

有時候,我們在寫C程序的時候需要對struct的大小做一些限制。比如說,struct需要以某些字節大小進行對齊,以符合硬件的支持(也許是某個設備的DMA緩衝區,要使用地址的低位做一些其他的計算),並且這些struct組成了一個數組,每個數組裏面的struct都需要以字節8對齊。

有時候我們可以這樣寫

#if ((sizeof(struct mystruct) % 8 ) != 0)
#error "you screwed up struct mystruct again!"
#endif
但是這樣不一定會成功,預處理器不一定會支持sizeof操作符,也不知道C的具體類型。或者,可以把它寫到運行時代碼裏面:

if ((sizeof(struct mystruct) % 8 ) != 0) {
    printf("You screwed up mystruct again!\n");
    exit(1);
}
但是這樣會產生額外的代碼,消耗不必要的CPU時間,並且這個問題直到運行時纔會發現這個BUG。

其實Linux內核裏面已經有相關的macro解決這個問題,在include/linux/kernel.h:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
使用這個黑科技,我們可以在編譯時就對struct大小進行檢查

int main()
{
    /* check that you didn't screw up mystruct */
    BUILD_BUG_ON(sizeof(struct mystruct) % 8));
    return 0;
}
如果struct不是8的倍數,這個在編譯時會報錯;如果它是struct的倍數,這個語句不會產生任何運行時代碼。完美符合我們的要求。

但是它是怎麼運行的呢?

比如說,

 sizeof(char[1]); /* this gives you the size of a */
                  /* character array of 1. */
 sizeof(char[-11]); /* this doesn't compile */
這兩條語句,第一條不會報錯,第二條會報錯。基於這個原理,我們就可以設計這個struct的sizeof檢查了。

PS:下面這些我參考原blog簡要解釋以下BUILD_BUG_ON的實現,就不一一翻譯了

!!(condition)
這個語句強制讓condition返回0或者1.

1 - 2*!!(condition)
這個就是條件符合時,大小爲0;,編譯不報錯;否則大小爲-1,編譯報錯。

1;

這個語句不會產生額外的語句,但是會有編譯時的警告

(void)1;
這個語句即使使用了 -Wall的編譯參數,也不會有警告。

因此,BUILD_BUIG_ON這個宏展開後等效於

(void)sizeof(char[1])
或者

(void)sizeof(char[-1])






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章