標籤:公眾號 解決 html 簡單 防止 嵌入式 linux核心 數加 關於
大家好,我是濤哥,歡迎閱讀《跟濤哥一起學嵌入式》第04集,今天聊聊面試題。
嵌入式C語言面試題中,大家經常會看到宏定義的考題。比如:定義一個宏,求兩個數中的最大數。別小看這個考題,雖然簡單,但是它卻陷阱不斷,時刻在考驗著你的C語言編程功底!根據你的答案,面試官對你的印象肯定不一樣。那下面我們看看各個不同版本的答案吧。
合格
對於學過C語言的同學,寫出這個宏基本上不是什麼難事,使用條件運算子就能完成:
#define MAX(x,y) x > y ? x : y
這是最基本的C語言文法,如果連這個也寫不出來,估計場面會比較尷尬。面試官為了緩解尷尬,一般會對你說:小夥子,你很棒,回去等訊息吧,有訊息,我們會通知你!這時候,你應該明白:不用再等了,趕緊把這篇文章看完,接著面下家。這個宏能寫出來,也不要覺得你很牛X,因為這隻能說明你有了C語言的基礎,但還有很大的進步空間。比如,我們寫一個程式,驗證一下我們定義的宏是否正確:
#define MAX(x,y) x > y ? x : yint main(void){ printf("max=%d",MAX(1,2)); printf("max=%d",MAX(2,1)); printf("max=%d",MAX(2,2)); printf("max=%d",MAX(1!=1,1!=2)); return 0;}
測試程式麼,我們肯定要把各種可能出現的情況都測一遍。這不,測試第4行語句,當宏的參數是一個運算式,發現實際運行結果為max=0,跟我們預期結果max=1不一樣。這是因為,宏展開後,就變成了這個樣子:
printf("max=%d",1!=1>1!=2?1!=1:1!=2);
因為比較子 > 的優先順序為6,大於 !=(優先順序為7),所以展開的運算式,運算順序發生了改變,結果就跟我們的預期不一樣了。為了避免這種展開錯誤,我們可以給宏的參數加一個小括弧()來防止展開後,運算式的運算順序發生變化。這樣的宏才能算一個合格的宏:
#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) 的值,預期結果應該是5,但實際運行結果卻是1。我們展開後,發現同樣有問題:
3 + (1) > (2) ? (1) : (2);
因為運算子 + 的優先順序大於比較子 >,所以這個運算式就變為4>2?1:2,最後結果為1也就見怪不怪了。此時我們應該繼續修改這個宏:
#define MAX(x,y) ((x) > (y) ? (x) : (y))
使用小括弧將宏定義包起來,這樣就避免了當一個運算式同時含有宏定義和其它高優先順序運算子時,破壞整個運算式的運算順序。如果你能寫到這一步,說明你比前面那個面試合格的同學強,前面那個同學已經回去等訊息了,我們接著面試下一輪。
良好
上面的宏,雖然解決了運算子優先順序帶來的問題,但是仍存在一定的漏洞。比如,我們使用下面的測試程式來測試我們定義的宏:
#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,然後比較兩個變數的大小,並作自增運算。實際運行結果發現max = 7,而不是預期結果max = 6。這是因為變數 i 和 j 在宏展開後,做了兩次自增運算,導致列印出 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來儲存宏參數 x 和 y 的值,然後使用 _x 和 _y 來比較大小,這樣就避免了 i 和 j 帶來的2次自增運算問題。
你能堅持到了這一關,並寫出這樣內建BGM的宏,面試官心裡可能已經有了給你offer的意願了。但此時此刻,千萬不要驕傲!為了徹底打消面試官的心理顧慮,我們需要對這個宏繼續最佳化。
優秀
在上面這個宏中,我們定義的兩個臨時VARIANT 資料型別是int型,只能比較兩個整型的資料。那對於其它類型的資料,就需要重新再定義一個宏了,這樣太麻煩了!我們可以基於上面的宏繼續修改,讓它可以支援任意類型的資料比較大小:
#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;}
在這個宏中,我們添加一個參數:type,用來指定臨時變數 _x 和 _y 的類型。這樣,我們在比較兩個數的大小時,只要將2個資料的類型作為參數傳給宏,就可以比較任意類型的資料了。如果你能在面試中,寫出這樣的宏,面試官肯定會非常高興,他一般會跟你說:小夥子,稍等,待會HR會跟你談待遇問題。
還能不能更牛逼?
如果你想薪水拿得高一點,待遇好一點,此時不應該驕傲,你應該大手一揮:且慢,我還可以更牛逼!
上面的宏定義中,我們增加了一個type型別參數,來相容不同的資料類型,此時此刻,為了薪水,我們應該把這個也省去。如何做到?使用typeof就可以了,typeof是GNU C新增的一個關鍵字,用來擷取資料類型,我們不用傳參進去,讓typeof直接擷取!
#define max(x, y) ({ typeof(x) _x = (x); typeof(y) _y = (y); (void) (&_x == &_y); _x > _y ? _x : _y; })
在這個宏定義中,使用了typeof關鍵字用來擷取宏的兩個參數類型。乾貨在(void) (&x == &y);這句話,簡直是天才般的設計!一是用來給使用者提示一個警告,對於不同類型的指標比較,編譯器會給一個警告,提示兩種資料類型不同;二是,當兩個值比較,比較的結果沒有用到,有些編譯器可能會給出一個warning,加個(void)後,就可以消除這個警告!
此刻,面試官看到你的這個宏,估計會倒吸一口氣:乖乖,果然是後生可畏,這傢伙比我還牛逼!你等著,HR待會過來跟你談薪水!
恭喜你,拿到offer了!
本文根據《C語言嵌入式Linux進階編程》第5期:C標準及Linux核心中的C文法擴充部分視頻改編。《跟濤哥一起學嵌入式》,會持續跟大家分享嵌入式相關技術、學習方法、學習路線、求職面試等,有興趣可加入嵌入式技術交流群:475504428,或公眾號:宅學部落(armlinuxfun)。或者關於51CTO學院我的個人首頁:http://edu.51cto.com/lecturer/10824150.html
跟濤哥一起學嵌入式 第04集:一道面試題,測出你的C語言功底