http://www.crifan.com/_define_detailed/
1.#define的變體,即#ifndef,可以防止頭頭文件的重復(fù)引用
[解釋]
#ifdef和#define組合,一般用于頭文件中,用以實(shí)現(xiàn)防止多個文件對此同一個頭文件的重復(fù)引用.實(shí)際使用中,即使你的頭文件暫時沒有被多個文件所引用,為了增加程序可讀性,移植性,健壯性等,還是最好都加上。其用法一般為:
#ifndef <</SPAN>標(biāo)識>
#define <</SPAN>標(biāo)識>
……… //include or define sth.
#endif
<</FONT>標(biāo)識>在理論上來說可以是自由命名的,但每個頭文件的這個“標(biāo)識”都應(yīng)該是唯一的
to void the definition duplication。butnormallz,標(biāo)識的命名規(guī)則一般是頭文件名全大寫,前后加下劃線,并把文件名中的“.”也變成下劃線,如:stdio.h對應(yīng)的就是:
#ifndef_STDIO_H_
#define_STDIO_H_
……… //include or define sth.
#endif
摘自網(wǎng)上一個例子如下:
Example 7-2.chardev.h
#ifndefCHARDEV_H
#defineCHARDEV_H
#include
#defineMAJOR_NUM100
……
……
#endif
例子中的chardev.h中,就用到了
#ifndefCHARDEV_H
#defineCHARDEV_H
…
#endif
這樣做之后,以后某個文件引用此頭文件,就包含了
#defineCHARDEV_H
而其他文件如果再引用此頭文件的話,編譯器那么就會判斷出來,已經(jīng)define了CHARDEV_H,已經(jīng)有其他文件引用了此文件,so,thecomplier will not include this header file.就可以發(fā)現(xiàn)解決重復(fù)引用頭文件的問題了.
2.#define的變體,即#ifdef,可以實(shí)現(xiàn)加入自己需要的模塊(源文件)
[例子]
在源文件中加入
#ifdefMYSELF_H
#include"myself.c"
#endif
可以實(shí)現(xiàn)在源文件中加入myself.c的代碼,將其實(shí)現(xiàn)的功能加進(jìn)來,即加入了myself模塊
3.#define可以進(jìn)行宏定義常量
[解釋]
可以對一些常見的變量,字符串等,進(jìn)行宏定義,系統(tǒng)在編譯期間,就會自動替換
如果不進(jìn)行宏定義,一般如果此類變量,字符串等,需要修改,就需要對源文件中它們出現(xiàn)的地方一一修改,效率比較低,而此種宏定義后,只需要修改一次,實(shí)現(xiàn)批量修改,效率較高.而且有些數(shù)字或字符很麻煩,每次都要輸入,就顯得很繁瑣,而且容易出錯,而采取此宏定義,就很方便和易于維護(hù).
[例子]
#define PI3.1415926
[注意事項]
(1)宏定義中的變量,約定俗成用大寫,以此與小寫的普通變量區(qū)分開來.當(dāng)然如果你故意小寫,也是合法的.不過如果你想讓你寫的程序具有高可讀性,那最好遵守此約定.
(2)#define的行尾,沒有分號”;”,有些人不注意,會畫蛇添足地加上.有些公司招聘時候的筆試,也會考察這個細(xì)節(jié).
(3)如果后面的宏定義中的變量和前面的有內(nèi)在聯(lián)系,那么后面的宏定義變量最好用前面的表示
[例子]
#define PI3.1415926
#define RADIUS5
而在表達(dá)該圓的面積的時候,就可以用下面的表示了:
#define AREA ((PI)*( RADIUS)*( RADIUS))
//此處加括號是為了避免后面提到的一種邊界效應(yīng)
[缺點(diǎn)]
宏定義有一些缺點(diǎn):
(1)無法對宏定義中的變量進(jìn)行類型檢查
此缺點(diǎn),是相對于const變量來說的
[define與const的區(qū)別的簡單總結(jié)]
define定義的變量,是Compile-Time時期的變量,系統(tǒng)在編譯時候,就將其全部替換,而不會對其變量進(jìn)行類型等屬性檢查,相對不是很安全,可能存在潛在的問題,而沒有發(fā)現(xiàn).
正因為其僅僅是編譯時期替換,所以其定義的變量,是不會在運(yùn)行時候分配內(nèi)存的,不占用內(nèi)存空間.
const定義的變量,是Run-Time時期的變量,如果類型不匹配,系統(tǒng)在運(yùn)行時候,就會發(fā)現(xiàn)并提示或報錯,對應(yīng)的,const變量在運(yùn)行時期,也是一種變量,系統(tǒng)會為其分配內(nèi)存.
(2)邊界效應(yīng)
A.未加括號帶來的邊界效應(yīng)
由于宏定義的時候,其各個分量未加括號,而在使用宏定義的時候,傳遞的參數(shù)是變量的表達(dá)式,然后經(jīng)過系統(tǒng)展開后,由于優(yōu)先級的原因,導(dǎo)致其結(jié)果不是你所希望的.
[例子]
#define MUL(A,B)A*B
而在使用的時候,這樣的調(diào)用:
inta=1,b=2,c=3,d=0;
d=MUL(a+b,c)
經(jīng)過編譯時候展開,就變成了
d=a+b*c
而不是我們所希望的
d=(a+b)*c
[解決辦法]
其解決辦法也很簡單,就是給每個分量,都加上括號,就可以避免此類問題
即,在宏定義的時候,如此定義:
#define MUL(A,B)((A)*(B))
B.在define數(shù)據(jù)類型的時候,未加括號帶來的問題
在用define進(jìn)行新的數(shù)據(jù)類型定義的時候,由于未加括號,會出現(xiàn)你所未預(yù)料到的結(jié)果.
此點(diǎn)其實(shí)就是上面說的邊界效應(yīng),之所以將此點(diǎn)單獨(dú)說一下,是由于此點(diǎn)不是普通計算結(jié)果的問題,而是數(shù)據(jù)類型的問題,問題相對更嚴(yán)重.
也是筆者在看《想成為嵌入式程序員應(yīng)知道的0×10個基本問題》的時候,看到其作者提到的這個問題,此處就用其例子解釋如下:
[例子]
#define dPS struct s*//注意末尾無分號
當(dāng)使用的時候,遇到:
dPS p1,p2;
的時候,經(jīng)過編譯時候替換擴(kuò)展,就變成了
struct s*p1,p2;
而p2就不是我們所希望的s的指針類型了,而是一個普通的s數(shù)據(jù)結(jié)構(gòu)類型的了.產(chǎn)生了邊界效應(yīng).
[解決辦法]
對應(yīng)的解決辦法也很簡單,就是,遇到此類新數(shù)據(jù)類型定義的時候,還是用typedef
將上述宏定義改為:
typedef struct s * tPS;// 注意末尾有分號
而后的使用:
tPS p1,p2;
就正常了.
C.特殊情況時候,加了括號也無法避免錯誤
在宏定義中出現(xiàn)++或—之類的操作符的時候,即使加括號,也無法避免其中的問題
[例子]
#define MIN(A,B)((A)<(B)?(A):(B))
如果進(jìn)行如此調(diào)用
int a=1,b=3,min=0;
min=MIN(a++,b);
經(jīng)過編譯替換展開后,就成了
max=((a++)<(b)?(a++):(b))
計算出來的結(jié)果,就是
min=3,而不是我們所要的min=1了.
此類問題無法避免,除非程序員在調(diào)用宏的時候,自己多加注意,盡量避免此類調(diào)用.
about how to use the Macro ofthe mostly used function, like min() and max() ,please referthis:
eliminate the side effect of Micro inC
【后記20100813】
如果想要min的宏定義,避免傳入a++,b++之類所導(dǎo)致的副作用,那么可以參考最新的Linux內(nèi)核中的定義,如下:
#define min(x, y)({
typeof(x) _x =(x);
typeof(y) _y =(y);
(void) (&_x ==&_y);
_x < _y ? _x : _y; })
關(guān)于其中(&_x == &_y);的作用,詳見:
【整理】min()的宏定義中的(void) (&_x ==&_y)的含義
http://hi.baidu.com/serial_story/blog/item/b6fd81098b5b1b296a60fb4d.html
愛華網(wǎng)


