今天在寫文件系統時,想在編譯時檢查一些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])