本章集中討論與預(yù)處理程序有關(guān)的問(wèn)題。在編譯程序?qū)Τ绦蜻M(jìn)行通常的編譯之前,要先運(yùn)行預(yù)處理程序??赡苣阋郧皼](méi)有見(jiàn)過(guò)這個(gè)程序,因?yàn)樗ǔT谀缓筮\(yùn)行,程序員是看不見(jiàn)它的,然而,這個(gè)程序非常有用。
預(yù)處理程序?qū)⒏鶕?jù)源代碼中的預(yù)處理指令來(lái)修改你的程序。預(yù)處理指令(如#define)為預(yù)處理程序提供特定的指令,告訴它應(yīng)該如何修改你的源代碼。預(yù)處理程序讀入所有包含的文件和待編譯的源代碼,經(jīng)過(guò)處理生成源代碼的預(yù)處理版本。在該版本中,宏和常量標(biāo)識(shí)符已用相應(yīng)的代碼和值代替。如果源代碼中包含條件預(yù)處理指令(如#if),預(yù)處理程序?qū)⑾扰袛鄺l件,然后相應(yīng)地修改源代碼。
預(yù)處理程序有許多非常有用的功能,例如宏定義,條件編譯,在源代碼中插入預(yù)定義的環(huán)境變量,打開(kāi)或關(guān)閉某個(gè)編譯選項(xiàng),等等。對(duì)專業(yè)程序員來(lái)說(shuō),深入了解預(yù)處理程序的各種特征,是創(chuàng)建快速和高效的程序的關(guān)鍵之一。
在閱讀本章時(shí),請(qǐng)記住本章采用的一些技術(shù)(以及所提到的一些常見(jiàn)陷阱),以便更好地利用預(yù)處理程序的各種功能。
5.1 什么是宏(macro)?怎樣使用宏?
宏是一種預(yù)處理指令,它提供了一種機(jī)制,可以用來(lái)替換源代碼中的字符串,宏是用“#define"語(yǔ)句定義的,下面是一個(gè)宏定義的例子:
#define VERSION—STAMP "1.02"
上例中所定義的這種形式的宏通常被稱為標(biāo)識(shí)符。在上例中,標(biāo)識(shí)符VERSION_STAMP即代表字符串"1.02"——在編譯預(yù)處理時(shí),源代碼中的每個(gè)VERSION_STAMP標(biāo)識(shí)符都將被字符串“1.02”替換掉。
以下是另一個(gè)宏定義的例子:
#define CUBE(x)((x),(x)*(x))
上例中定義了一個(gè)名為CUBE的宏,它有一個(gè)參數(shù)x。CUBE宏有自己的宏體,即((x)*(x)*(x))——在編譯預(yù)處理時(shí),源代碼中的每個(gè)CUBE(x)宏都將被((x)*(x)*(x))替換掉。
使用宏有以下幾點(diǎn)好處;
(1)在輸入源代碼時(shí),可省去許多鍵入操作。
(2)因?yàn)楹曛恍瓒x一次,但可以多次使用,所以使用宏能增強(qiáng)程序的易讀性和可靠性。
(3)使用宏不需要額外的開(kāi)銷,因?yàn)楹晁淼拇a只在宏出現(xiàn)的地方展開(kāi),因此不會(huì)引起程序中的跳轉(zhuǎn)。
(4)宏的參數(shù)對(duì)類型不敏感,因此你不必考慮將何種數(shù)據(jù)類型傳遞給宏。
需要注意的是,在宏名和括起參數(shù)的括號(hào)之間絕對(duì)不能有空格。此外,為了避免在翻譯宏時(shí)產(chǎn)生歧義,宏體也應(yīng)該用括號(hào)括起來(lái)。例如,象下例中這樣定義CUBE宏是不正確的:
denne CUBE(x) x * x * x
對(duì)傳遞給宏的參數(shù)也要小心,例如,一種常見(jiàn)的錯(cuò)誤就是將自增變量傳遞給宏,請(qǐng)看下例:
#include
#include CUBE(x) (x * x * x)
void main (void);
void main (void)
{
int x, y;
x = 5;
y = CUBE( + +x);
printfC’y is %d\n" . y);
}
在上例中,y究竟等于多少呢?實(shí)際上,y既不等于125(5的立方),也不等于336(6* 7*8),而是等于512。因?yàn)樽兞縳被作為參數(shù)傳遞給宏時(shí)進(jìn)行了自增運(yùn)算,所以上例中的CUBE宏實(shí)際上是按以下形式展開(kāi)的:
y = ((++x) * (++x) * (++x));
這樣,每次引用x時(shí),x都要自增,所以你得到的結(jié)果與你預(yù)期的結(jié)果相差很遠(yuǎn),在上例中,由于x被引用了3次,而且又使用了自增運(yùn)算符,因此,在展開(kāi)宏的代碼時(shí),x實(shí)際上為8,你將得到8的立方,而不5的立方。
上述錯(cuò)誤是比較常見(jiàn)的,作者曾親眼見(jiàn)過(guò)有多年C語(yǔ)言編程經(jīng)驗(yàn)的人犯這種錯(cuò)誤。因?yàn)樵诔绦蛑袡z查這種錯(cuò)誤是非常費(fèi)勁的,所以你要給予充分的注意。你試一下上面的例子,親眼看一下那個(gè)令人驚訝的結(jié)果值(512)。
宏也可使用一些特殊的運(yùn)算符,例如字符串化運(yùn)算符“#”和。連接運(yùn)算符“##”?!?”運(yùn)算符能將宏的參數(shù)轉(zhuǎn)換為帶雙引號(hào)的字符串,請(qǐng)看下例:
define DEBUG_VALUE(v) printf(#v"is equal to %d.\n",v)
你可以在程序中用DEBUG_VALUE宏檢查變量的值,請(qǐng)看下例:
int x=20;
DEBUG_VALUE(x);
上述語(yǔ)句將在屏幕上打印"x is equal to 20"。這個(gè)例子說(shuō)明,宏所使用的“#”運(yùn)算符是一種非常方便的調(diào)試工具。
“##”運(yùn)算符的作用是將兩個(gè)獨(dú)立的字符串連接成一個(gè)字符串,詳見(jiàn)5.16。
預(yù)處理程序?qū)⒏鶕?jù)源代碼中的預(yù)處理指令來(lái)修改你的程序。預(yù)處理指令(如#define)為預(yù)處理程序提供特定的指令,告訴它應(yīng)該如何修改你的源代碼。預(yù)處理程序讀入所有包含的文件和待編譯的源代碼,經(jīng)過(guò)處理生成源代碼的預(yù)處理版本。在該版本中,宏和常量標(biāo)識(shí)符已用相應(yīng)的代碼和值代替。如果源代碼中包含條件預(yù)處理指令(如#if),預(yù)處理程序?qū)⑾扰袛鄺l件,然后相應(yīng)地修改源代碼。
預(yù)處理程序有許多非常有用的功能,例如宏定義,條件編譯,在源代碼中插入預(yù)定義的環(huán)境變量,打開(kāi)或關(guān)閉某個(gè)編譯選項(xiàng),等等。對(duì)專業(yè)程序員來(lái)說(shuō),深入了解預(yù)處理程序的各種特征,是創(chuàng)建快速和高效的程序的關(guān)鍵之一。
在閱讀本章時(shí),請(qǐng)記住本章采用的一些技術(shù)(以及所提到的一些常見(jiàn)陷阱),以便更好地利用預(yù)處理程序的各種功能。
5.1 什么是宏(macro)?怎樣使用宏?
宏是一種預(yù)處理指令,它提供了一種機(jī)制,可以用來(lái)替換源代碼中的字符串,宏是用“#define"語(yǔ)句定義的,下面是一個(gè)宏定義的例子:
#define VERSION—STAMP "1.02"
上例中所定義的這種形式的宏通常被稱為標(biāo)識(shí)符。在上例中,標(biāo)識(shí)符VERSION_STAMP即代表字符串"1.02"——在編譯預(yù)處理時(shí),源代碼中的每個(gè)VERSION_STAMP標(biāo)識(shí)符都將被字符串“1.02”替換掉。
以下是另一個(gè)宏定義的例子:
#define CUBE(x)((x),(x)*(x))
上例中定義了一個(gè)名為CUBE的宏,它有一個(gè)參數(shù)x。CUBE宏有自己的宏體,即((x)*(x)*(x))——在編譯預(yù)處理時(shí),源代碼中的每個(gè)CUBE(x)宏都將被((x)*(x)*(x))替換掉。
使用宏有以下幾點(diǎn)好處;
(1)在輸入源代碼時(shí),可省去許多鍵入操作。
(2)因?yàn)楹曛恍瓒x一次,但可以多次使用,所以使用宏能增強(qiáng)程序的易讀性和可靠性。
(3)使用宏不需要額外的開(kāi)銷,因?yàn)楹晁淼拇a只在宏出現(xiàn)的地方展開(kāi),因此不會(huì)引起程序中的跳轉(zhuǎn)。
(4)宏的參數(shù)對(duì)類型不敏感,因此你不必考慮將何種數(shù)據(jù)類型傳遞給宏。
需要注意的是,在宏名和括起參數(shù)的括號(hào)之間絕對(duì)不能有空格。此外,為了避免在翻譯宏時(shí)產(chǎn)生歧義,宏體也應(yīng)該用括號(hào)括起來(lái)。例如,象下例中這樣定義CUBE宏是不正確的:
denne CUBE(x) x * x * x
對(duì)傳遞給宏的參數(shù)也要小心,例如,一種常見(jiàn)的錯(cuò)誤就是將自增變量傳遞給宏,請(qǐng)看下例:
#include
#include CUBE(x) (x * x * x)
void main (void);
void main (void)
{
int x, y;
x = 5;
y = CUBE( + +x);
printfC’y is %d\n" . y);
}
在上例中,y究竟等于多少呢?實(shí)際上,y既不等于125(5的立方),也不等于336(6* 7*8),而是等于512。因?yàn)樽兞縳被作為參數(shù)傳遞給宏時(shí)進(jìn)行了自增運(yùn)算,所以上例中的CUBE宏實(shí)際上是按以下形式展開(kāi)的:
y = ((++x) * (++x) * (++x));
這樣,每次引用x時(shí),x都要自增,所以你得到的結(jié)果與你預(yù)期的結(jié)果相差很遠(yuǎn),在上例中,由于x被引用了3次,而且又使用了自增運(yùn)算符,因此,在展開(kāi)宏的代碼時(shí),x實(shí)際上為8,你將得到8的立方,而不5的立方。
上述錯(cuò)誤是比較常見(jiàn)的,作者曾親眼見(jiàn)過(guò)有多年C語(yǔ)言編程經(jīng)驗(yàn)的人犯這種錯(cuò)誤。因?yàn)樵诔绦蛑袡z查這種錯(cuò)誤是非常費(fèi)勁的,所以你要給予充分的注意。你試一下上面的例子,親眼看一下那個(gè)令人驚訝的結(jié)果值(512)。
宏也可使用一些特殊的運(yùn)算符,例如字符串化運(yùn)算符“#”和。連接運(yùn)算符“##”?!?”運(yùn)算符能將宏的參數(shù)轉(zhuǎn)換為帶雙引號(hào)的字符串,請(qǐng)看下例:
define DEBUG_VALUE(v) printf(#v"is equal to %d.\n",v)
你可以在程序中用DEBUG_VALUE宏檢查變量的值,請(qǐng)看下例:
int x=20;
DEBUG_VALUE(x);
上述語(yǔ)句將在屏幕上打印"x is equal to 20"。這個(gè)例子說(shuō)明,宏所使用的“#”運(yùn)算符是一種非常方便的調(diào)試工具。
“##”運(yùn)算符的作用是將兩個(gè)獨(dú)立的字符串連接成一個(gè)字符串,詳見(jiàn)5.16。