復(fù)雜表達(dá)式可能會(huì)被用作宏參數(shù),這可能會(huì)因操作符優(yōu)先級(jí)順序而引發(fā)問題,除非宏定義中所有參數(shù)出現(xiàn)的位置都用括號(hào)括上了蛔翅。對(duì)這種因參數(shù)內(nèi)副作用而引發(fā)的問題恼布,我們似乎也無能為例,除了在編寫表達(dá)式時(shí)杜絕副作用(無論如何搁宾,這都是一個(gè)很好的主意)折汞。如果可能的話,盡量在宏定義中對(duì)宏參數(shù)只進(jìn)行一次求值盖腿。有很多時(shí)候我們無法寫出一個(gè)可像函數(shù)一樣使用的宏爽待。
一些宏也當(dāng)成函數(shù)使用(例如,getc和fgetc)翩腐。這些宏會(huì)被用于實(shí)現(xiàn)其他函數(shù)鸟款,這樣一旦宏自身發(fā)生變化,使用該宏的函數(shù)也會(huì)受到影響茂卦。在交換宏和函數(shù)時(shí)務(wù)必要小心何什,因?yàn)楹瘮?shù)參數(shù)是按值傳遞的,而宏參數(shù)則是通過名稱替換等龙。只有在宏定義時(shí)特別謹(jǐn)慎小心处渣,才有可能減少使用宏時(shí)的擔(dān)心。
宏定義中應(yīng)該避免使用全局變量蛛砰,因?yàn)槿肿兞康拿趾芸赡鼙痪植柯暶髡谏w罐栈。對(duì)于那些對(duì)具名參數(shù)進(jìn)行修改(不是這些參數(shù)所指向的存儲(chǔ)區(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)一個(gè)宏過長時(shí)柑贞,函數(shù)調(diào)用和返回的額外開銷就變得微不足道了,這種情況下我們應(yīng)該使用函數(shù)聂抢。
在一些情況下钧嘶,讓編譯器確保宏在使用時(shí)應(yīng)該以分號(hào)結(jié)尾是很有必要的。
if (x==3)
SP3();
else
BORK();
如果省略SP3調(diào)用后面的分號(hào)涛浙,后面的else將會(huì)匹配到SP3宏中的那個(gè)if康辑。有了分號(hào),else分支就不會(huì)與任何if匹配轿亮。SP3宏可以這樣安全地實(shí)現(xiàn):
#define SP3() \\\\
do { if (b) { int x; av = f (&x); bv += x; }} while (0)
手工給宏定以加上do-while包圍看起來很別扭疮薇,而且很多編譯器和工具會(huì)抱怨在while條件是一個(gè)常量值。一個(gè)用來聲明語句的宏可以使得編碼更加容易:
#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宏可以有效阻止一些可以潛在改變程序行為的打印排版錯(cuò)誤我注。
除了類型轉(zhuǎn)換按咒、sizeof以及上面那些技巧和手法,只有當(dāng)整個(gè)宏用括號(hào)括上時(shí)才應(yīng)該包含關(guān)鍵字但骨。