很多時候當你完成了C語言語法的學(xué)習(xí),特別適合一些擴展用法之后熊楼,自身會陷入一個自滿的大坑鸭叙,以為自己啥都會了觉啊,但其實自己水平到底怎樣,你自己也不知道沈贝。那么今天我就帶著大家測一測你的嵌入式C學(xué)的到底有多牛(low)杠人。
自測題:請定義一個宏,求兩個數(shù)中的最大數(shù)宋下。
不合格水平
對于學(xué)過C語言的同學(xué)嗡善,寫出這個宏基本上不是什么難事,使用條件運算符就能完成:
#define MAX(x,y) x > y ? x : y
這是最基本的C語言語法学歧,如果連這個也寫不出來罩引,估計你還沒進C的大門。但一般這種水平枝笨,公司是不會要你的袁铐,因為你還是門外漢!
不信横浑?舉個反例剔桨,驗證一下我們定義的宏是否正確:
#define MAX(x,y) x > y ? x : y
int main(void)
{
printf("max=%d",MAX(1!=1,1!=2));
return 0;
}
當宏的參數(shù)是一個表達式,發(fā)現(xiàn)實際運行結(jié)果為max=0,跟我們預(yù)期結(jié)果max=1不一樣伪嫁。這是因為领炫,宏展開后,就變成了這個樣子:
printf("max=%d",1!=1>1!=2?1!=1:1!=2);
因為比較運算符 > 的優(yōu)先級為6张咳,大于 !=(優(yōu)先級為7)帝洪,所以展開的表達式,運算順序發(fā)生了改變脚猾,結(jié)果就跟我們的預(yù)期不一樣了葱峡。
合格水平
為了避免這種展開錯誤,我們可以給宏的參數(shù)加一個小括號()來防止展開后龙助,表達式的運算順序發(fā)生變化砰奕。這樣的宏才能算一個合格的宏:
#define MAX(x,y) (x) > (y) ? (x) : (y)
但上面的宏還是存在漏洞。
比如提鸟,我們使用下面的代碼測試:
#define MAX(x,y) (x) > (y) ? (x) : (y)
int main(void)
{
printf("max=%d",3 + MAX(1,2));
return 0;
}
在程序中军援,我們打印表達式 3 + MAX(1, 2) 的值,預(yù)期結(jié)果應(yīng)該是5称勋,但實際運行結(jié)果卻是1胸哥。我們展開后,發(fā)現(xiàn)同樣有問題:
3 + (1) > (2) ? (1) : (2);
因為運算符 + 的優(yōu)先級大于比較運算符 >赡鲜,所以這個表達式就變?yōu)?>2?1:2空厌,最后結(jié)果為1也就見怪不怪了庐船。
中等水平
我們繼續(xù)修改這個宏:
#define MAX(x,y) ( (x) > (y) ? (x) : (y) )
使用小括號將宏定義包起來,這樣就避免了當一個表達式同時含有宏定義和其它高優(yōu)先級運算符時嘲更,破壞整個表達式的運算順序筐钟。
上面的宏,雖然解決了運算符優(yōu)先級帶來的問題赋朦,但是仍存在一定的漏洞篓冲。
比如:
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
int i = 2;
int j = 6;
printf("max=%d",MAX(i++,j++));
return 0;
}
在程序中,我們定義兩個變量 i 和 j北发,然后比較兩個變量的大小纹因,并作自增運算。實際運行結(jié)果發(fā)現(xiàn)max = 7琳拨,而不是預(yù)期結(jié)果max = 6瞭恰。這是因為變量 i 和 j 在宏展開后,做了兩次自增運算狱庇,導(dǎo)致打印出 i 的值為7惊畏。
良好水平
遇到這種情況,語句表達式就該上場了密任。我們可以使用語句表達式來定義這個宏颜启,在語句表達式中定義兩個臨時變量,分別來暫儲 i 和 j 的值浪讳,然后進行比較缰盏,這樣就避免了兩次自增、自減問題淹遵。
#define MAX(x,y)({ \
int _x = x; \
int _y = y; \
_x > _y ? _x : _y; \
})
int main(void)
{
int i = 2;
int j = 6;
printf("max=%d",MAX(i++,j++));
return 0;
}
在語句表達式中口猜,我們定義了2個局部變量_x、_y來存儲宏參數(shù) x 和 y 的值透揣,然后使用 _x 和 _y 來比較大小济炎,這樣就避免了 i 和 j 帶來的2次自增運算問題。
如果你能堅持到了這一關(guān)辐真,并寫出這樣自帶BGM的宏须尚,說明你有了比較好的語言基礎(chǔ),是可雕琢一塊木頭侍咱,面試官心里可能已經(jīng)有了給你offer的意愿了耐床。
優(yōu)秀水平
在上面這個宏中,我們定義的兩個臨時變量數(shù)據(jù)類型是int型楔脯,只能比較兩個整型的數(shù)據(jù)撩轰。那對于其它類型的數(shù)據(jù),就需要重新再定義一個宏了,這樣太麻煩了钧敞!我們可以基于上面的宏繼續(xù)修改,讓它可以支持任意類型的數(shù)據(jù)比較大恤锪浮:
#define MAX(type,x,y) ( { \
type _x = x; \
type _y = y; \
_x > _y ? _x : _y; \
})
int main(void)
{
int i = 2;
int j = 6;
printf("max=%d\n",MAX(int,i++,j++));
printf("max=%f\n",MAX(float,3.14,3.15));
return 0;
}
在這個宏中溉苛,我們添加一個參數(shù):type,用來指定臨時變量 _x 和 _y 的類型弄诲。這樣愚战,我們在比較兩個數(shù)的大小時,只要將2個數(shù)據(jù)的類型作為參數(shù)傳給宏齐遵,就可以比較任意類型的數(shù)據(jù)了寂玲。如果你能寫出這樣的宏,說明你是可雕琢的一塊玉梗摇,試官肯定會非常高興他遇到了你拓哟。
超優(yōu)秀水平
上面的宏定義中,我們增加了一個type類型參數(shù)伶授,來兼容不同的數(shù)據(jù)類型,此時此刻,為了這個超畔咧,我們應(yīng)該把這個也省去某抓。
如何做到?使用typeof就可以了疮蹦,typeof是GNU C新增的一個關(guān)鍵字诸迟,用來獲取數(shù)據(jù)類型,我們不用傳參進去愕乎,讓typeof直接獲日笪!
#define max(x, y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y);\
_x > _y ? _x : _y;
})
在這個宏定義中妆毕,使用了typeof關(guān)鍵字用來獲取宏的兩個參數(shù)類型慎玖。干貨在(void) (&x == &y);這句話,簡直是天才般的設(shè)計笛粘!一是用來給用戶提示一個警告趁怔,對于不同類型的指針比較,編譯器會給一個警告薪前,提示兩種數(shù)據(jù)類型不同润努;二是,當兩個值比較示括,比較的結(jié)果沒有用到時铺浇,有些編譯器可能會給出一個warning,加個(void)后垛膝,就可以消除這個警告