最近閑來無事,分析下linux kernel里面一些函數(shù)都是怎樣定義使用的,它們都是怎樣避免風險的,從include/linux/kernel.h中挑出一個經(jīng)典的min宏進行分析一下
 
從我們最先認識的min函數(shù)開始看看,這個函數(shù)的作用就是求兩個數(shù)中小一點的那個
#define min(x, y) ({                                \
typeof(x) _min1 = (x);                        \
typeof(y) _min2 = (y);                        \
(void) (&_min1 == &_min2);                \
_min1 < _min2 ? _min1 : _min2; })
 
1)首先我們可以看到這個宏的定義外面由一個({}),為什么要這樣寫呢?其實{}很好理解,因為我們的宏是多個語句,假如沒有了這個{},在一些語句中就會出現(xiàn)歧義,比如 if(a) min(x,y),這樣就會出問題,后面的語句它就不會執(zhí)行了。那么這里為什么在外面還要加一個()呢,我們來看這個函數(shù)的作用,它是用來返回一個較小的數(shù),對,是返回,一般來說對于返回值,我們怎么用?最簡單的當然是return了,所以,一般來講,我們會寫return min(x,y);(當然還有很多常用的方法,比如說賦值等等,我這邊的目的只是解釋()呵呵~~)。這個時候你就不難理解我們?yōu)槭裁匆觽€()了吧,不過正規(guī)的來講,這是GCC擴展中的一個用法,在GCC擴展中允許吧一個()內(nèi)的復合語句看成是一個表達式,稱為語句表達式,它可以出現(xiàn)在任何語句表達式可以出現(xiàn)的地方。
2)繼續(xù)看這個宏,我們知道一般我們使用這個函數(shù)的時候,x,y是什么類型的并不清楚,我們是否需要所有的類型都寫一個min函數(shù)呢?其實不需要,在GCC的擴展中,我們提供了一個宏,稱為typeof,它可以獲得相應參數(shù)的類型,這樣就可以不需要因為類型而重寫宏了,小方便啊。
再看這兩句話:
typeof(x) _min1 = (x);                        \
typeof(y) _min2 = (y);                        \
為什么要把x,y賦值給_min1和_min2然后返回_min1和_min2,而不直接寫成(x) < (y)? (x) :(y)呢?這個就是最經(jīng)典的C語言課本中所說的那個所謂的++的問題了,還需要多說嗎?好吧,我說一下吧,因為我也是菜鳥,也記不清當時是在什么地方提到這個東西了,呵呵,不過印象中記得好像是這樣說的:假如我寫成min(a++,b++),展開成宏就變成了(a++) < (b++)? (a++) :(b++)了,究竟a和b各自++了多少次,大概可以算出來吧,所以我們一定要先把它們賦值給兩個局部變量,這樣就沒有這個問題了。
3)下面再來看一下那個(void) (&_min1 == &_min2);這句話好糾結(jié)啊,奶奶的,反正我是沒看懂是什么作用,不過可以肯定的是肯定有它存在的意義,于是只好做試驗了,好吧,我寫寫寫,寫了好幾個代碼發(fā)現(xiàn)兩者都是一樣的,怎么辦呢?呵呵,google一下,終于發(fā)現(xiàn)有人講這個了,說是為了在比較不同類型的時候能夠打印一個警告信息。好吧,我試試看,寫一個簡單測試程序
#include <stdio.h>
 
#define min(x, y) ({                                \
typeof(x) _min1 = (x);                        \
typeof(y) _min2 = (y);                        \
(void) (&_min1 == &_min2);                \
_min1 < _min2 ? _min1 : _min2; })
 
main(){
unsigned long a = 4;
in b = 5;
 
printf("min is %d\n", min(a, b));
}
用gcc編譯了一下,果然報下面警告了:
min.c: In function ‘main’:
min.c:14: 警告:比較不相關(guān)的指針時缺少類型轉(zhuǎn)換
沒有這句話,就不會報這個警告,為什么會這樣呢?跟阿虛討論了下,原來,在比較地址的時候,會發(fā)現(xiàn)地址所存值的類型,假如不同的話就會報警告,而在直接比較值的時候,比如上面的_min1 < _min2 ?的時候,會做強制類型轉(zhuǎn)換,而且不會報警告,好吧,我真的很佩服寫這段代碼的人,太牛了!簡單的4,5行代碼,竟然有這么多的東西在里面,這叫老夫如何是好啊,繼續(xù)努力。