1. 程式人生 > >C++老鳥日記029 你確定要使用巨集嗎?

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著