13. 宏

復(fù)雜表達(dá)式可能會被用作宏參數(shù),這可能會因操作符優(yōu)先級順序而引發(fā)問題,除非宏定義中所有參數(shù)出現(xiàn)的位置都用括號括上了。對這種因參數(shù)內(nèi)副作用而引發(fā)的問題,我們似乎也無能為例,除了在編寫表達(dá)式時杜絕副作用(無論如何,這都是一個很好的主意)。如果可能的話,盡量在宏定義中對宏參數(shù)只進(jìn)行一次求值。有很多時候我們無法寫出一個可像函數(shù)一樣使用的宏。

一些宏也當(dāng)成函數(shù)使用(例如,getc和fgetc)。這些宏會被用于實(shí)現(xiàn)其他函數(shù),這樣一旦宏自身發(fā)生變化,使用該宏的函數(shù)也會受到影響。在交換宏和函數(shù)時務(wù)必要小心,因?yàn)楹瘮?shù)參數(shù)是按值傳遞的,而宏參數(shù)則是通過名稱替換。只有在宏定義時特別謹(jǐn)慎小心,才有可能減少使用宏時的擔(dān)心。

宏定義中應(yīng)該避免使用全局變量,因?yàn)槿肿兞康拿趾芸赡鼙痪植柯暶髡谏w。對于那些對具名參數(shù)進(jìn)行修改(不是這些參數(shù)所指向的存儲區(qū)域)或被用作賦值語句左值的宏,我們應(yīng)該添加相應(yīng)的注釋以給予提醒。那些不帶參數(shù)但引用變量,或過長或作為函數(shù)別名的宏應(yīng)該使用空參數(shù)列表,例如:

#define    OFF_A()    (a_global+OFFSET)
#define    BORK()    (zork())
#define    SP3()    if (b) { int x; av = f (&x); bv += x; }

宏節(jié)省了函數(shù)調(diào)用和返回的額外開銷,但當(dāng)一個宏過長時,函數(shù)調(diào)用和返回的額外開銷就變得微不足道了,這種情況下我們應(yīng)該使用函數(shù)。

在一些情況下,讓編譯器確保宏在使用時應(yīng)該以分號結(jié)尾是很有必要的。

if (x==3)
    SP3();
else
    BORK();

如果省略SP3調(diào)用后面的分號,后面的else將會匹配到SP3宏中的那個if。有了分號,else分支就不會與任何if匹配。SP3宏可以這樣安全地實(shí)現(xiàn):

#define SP3() \\\\
     do { if (b) { int x; av = f (&x); bv += x; }} while (0)

手工給宏定以加上do-while包圍看起來很別扭,而且很多編譯器和工具會抱怨在while條件是一個常量值。一個用來聲明語句的宏可以使得編碼更加容易:

#ifdef lint
    static int ZERO;
#else
#    define ZERO 0
#endif
#define STMT( stuff )        do { stuff } while (ZERO)

我們可以用下面代碼來聲明SP3宏:

#define SP3() \\\\
    STMT( if (b) { int x; av = f (&x); bv += x; } )

使用STMT宏可以有效阻止一些可以潛在改變程序行為的打印排版錯誤。

除了類型轉(zhuǎn)換、sizeof以及上面那些技巧和手法,只有當(dāng)整個宏用括號括上時才應(yīng)該包含關(guān)鍵字。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容