C語(yǔ)言中宏是一個(gè)很好的工具带猴,但也容易引起錯(cuò)誤荚斯。
1. 宏不是函數(shù)
#define max(a,b) ((a)>(b)?(a):(b))
請(qǐng)注意宏定義中的括號(hào)惩猫,它們的作用就是預(yù)防引起與優(yōu)先級(jí)有關(guān)的問(wèn)題陨晶。沒(méi)有這些括號(hào)猬仁,當(dāng)宏展開(kāi)的時(shí)候就可能引起意外的結(jié)果
biggest = x[0];
i = 1;
while(i < n)
biggest = max(biggest, x[i++]);
如果max是一個(gè)真正的函數(shù),上面的代碼可以正常工作先誉,但是max如果是一個(gè)宏湿刽,那么就不能正常工作。展開(kāi)一下:
biggest = ((biggest)>(x[i++])?(biggest):(x[i++]));
首先變量biggest與x[i++]比較褐耳。假設(shè)此時(shí)i值為1诈闺,同時(shí)biggest小于x[1],那么關(guān)系運(yùn)算結(jié)果為false铃芦。注意雅镊,因?yàn)閕++有副作用,在比較后i遞增為2刃滓,所以三目運(yùn)算結(jié)果是x[2]仁烹。這明顯不符合我們的預(yù)期,同時(shí)冒號(hào)后面的表達(dá)式還要經(jīng)歷一次副作用咧虎,整條語(yǔ)句結(jié)束后i的值是3卓缰。
而解決此類(lèi)問(wèn)題的一個(gè)辦法是,確保max中的參數(shù)沒(méi)有副作用。
2. 宏不是語(yǔ)句
__FILE__
和__LINE__
是內(nèi)建于C語(yǔ)言預(yù)處理器中的宏征唬。
assert宏
#define assert(e) if (震叮!(e)) assert_error(__FILE__,__LINE__)
assert宏這個(gè)定義,即使用在一個(gè)再明白不過(guò)的情形中鳍鸵,也會(huì)有一些難以察覺(jué)的錯(cuò)誤:
if (x > 0 && y > 0)
assert(x > y);
else
assert(y > x);
但是將其展開(kāi)并左適當(dāng)?shù)目s排處理后,看一下:
if (x > 0 && y > 0)
if(! (x > y))
assert_error("foo.c",37);
else
if(! (y > x))
assert_error("foo.c",39);
因?yàn)镃語(yǔ)言規(guī)定尉间,else與它上面最相鄰的if語(yǔ)句對(duì)齊偿乖。
所以這樣的情況下,還是老實(shí)一點(diǎn)填寫(xiě)上括號(hào)比較好哲嘲。
3.宏并不是類(lèi)型定義
#define T1 struct foo *
typedef struct foo * T2;
從上面兩個(gè)定義來(lái)看贪薪,T1和T2從概念上完全相同,都是指向結(jié)構(gòu)foo的指針眠副。但是画切,當(dāng)我們?cè)噲D用它們來(lái)聲明多個(gè)變量時(shí),問(wèn)題來(lái)了:
T1 a, b;
T2 a, b;
第一個(gè)聲明被擴(kuò)展為:
struct foo *a, b;
第二個(gè)聲明則不同囱怕,它定義了a和b都是指向結(jié)構(gòu)的指針霍弹,因?yàn)檫@里T2的行為完全與一個(gè)真實(shí)的類(lèi)型相同。