C++老鳥日記029 你確定要使用巨集嗎?
微信公眾號: 星點課堂
新浪微博:女兒叫老白
網易雲課堂:女兒叫老白
網易雲課堂免費課程:《C++跨平臺開發中的編譯錯誤》
網易雲課堂免費課程:《C++老鳥日記》
----------------------------------------------------------------------------
引言:
----------------------------------------------------------------------------
C++中的預處理巨集提供了一種手段,可以供我們預先定義一些字串來代替程式碼。但是,您知道嗎,使用預處理巨集經常回帶來一些意想不到的問題,現在我們來一起看一下。
正文:
----------------------------------------------------------------------------
使用預處理巨集時會有幾個潛在的風險:
1. 編寫程式碼時筆誤,導致巨集中間有空格,比如:
本來是:
#define PLUSX(x) (x+1)
卻因筆誤,寫成了:
#define PLUSX (x) (x+1)
請注意PLUSX 跟(x)之間有空格
那麼,PLUSX(2),展開後變成:
(x) (x+1)(2)
這不是我們所期望的結果。
2. 忽視了運算子優先順序的問題,
#define JUDGE(X, y) (x>y ? 1 : 0)
假定,我們傳入JUDGE(a&0x1, 0xf),展開後,變成:
(a&0x1 > 0xf ? 1 : 0)
但是因為>的優先順序高於&,所以 變成了 a& (0x1 > 0xf),這也不是我們所期望的,解決的方法是使用括號確定優先順序,比如上面的巨集改成:
#define JUDGE(x, y) (((x) > (y)) ? 1 : 0)
3. 本來想把巨集當作函式用,但是卻忽視了巨集定義與函式的區別。
#define SECTION_JUDGE(X) ((((x)>1) && ((x)<3)) ? 1 : 0)
如果我們傳入SECTION_JUDGE(++b),那麼巨集展開後變成:
((((++b)>1) && ((++b)<3)) ? 1 : 0
這也不是我們所期望的,我們本來期望傳入的值為b+1,然後b就不變了,但是使用這個巨集之後,b自加了兩次。
建議所有巨集採用全部大寫字母命名,也是為了降低這種風險,因為一看到程式碼就知道這是一個巨集定義,從而加倍小心。
有時候,我們使用巨集是因為它可以在呼叫處隨時展開,沒有函式呼叫的開銷;而行內函數也具備這樣的特性。行內函數也是在呼叫處展開程式碼,也沒有函式呼叫開銷。從這個意義上來說,建議大家永遠不使用巨集,只使用行內函數。
結語:
----------------------------------------------------------------------------
巨集定義有它的好處,比如縮短程式碼,降低函式呼叫開銷等。但是一不小心就會帶來潛在的問題,因此使用時要加倍小心。我個人不太傾向使用巨集,如果使用常量,我一般使用下面的方法:
static const double c_PI = 3.1415927
有不少其他的替代方案可以取代巨集定義的方案,因此,我們要開闊一下思路。祝大家在使用巨集時一切順利。
參考資料
----------------------------------------------------------------------------
《C++程式設計思想》兩卷合訂本中文版(9.1章節,以及P208, P210.),(美) Bruce Eckel Chuck Allison著