第九章C語言的預編譯語句(1 / 3)

第九章C語言的預編譯語句

在C語言的源程序中,除了說明語句和可執行語句外,還有一種特殊語句,稱為預編譯語句。預編譯語句的作用是告訴編譯係統,在正式編譯源程序之前,應該首先做些什麼預處理工作。這些預編譯語句雖不是C語言的組成部分,但卻擴充了C語言的程序設計環境。

ANSI標準定義的C語言包括如下4類預編譯語句:

(1)文件包括語句

# include

(2)宏定義語句

# define,# undef

(3)條件編譯語句

# if - # else - # endif

# ifdef - # else - # endif

# ifndef - # else - # endif

(4)其他預編譯語句

# line,# pragma

為了把預編譯語句C語言的語句區分開來,預編譯語句都用符號“#”開頭。

一個預編譯語句占用一行,其結尾不用分號“;”作結束。在編碼時,一個語句行一般從一行的首列位置開始,“#”號前麵不留空格。C編譯係統對源程序的處理過程是先進行預編譯,然後進行編譯。預編譯後生成的源文件中就不包含預編譯語句了,而完全是C語言的語句。

9.1 文件包括語句

在前麵各章中,曾經不隻一次見到這樣的語句:

# include

其含義是在預編譯時用stdio.h頭文件的內容替換該語句。

文件包括語句的一般格式是:

# include

# include 0169filename0169

其中filename是被包括文件的文件名,它是一個磁盤文件。該預編譯語句的功能是要將filename文件的全部內容包含在該# include語句所在的源文件中。更確切地說是在預編譯時,用filename文件的全部內容替換該# include語句行,使該文件成為這個源文件的一部分。

在# include語句的書寫格式中,被包括文件的文件名(即filename)可用尖括號(<、>)括住,也可用比引號(0169、0169)括住。當用尖括號時,其含義是指示編譯係統按係統設定的標準目錄搜索文件filename。而用雙引號括住時,表示按指定的路徑搜索;若未指定路徑名時,則在當前目錄中搜索。例如:

(1)從指定的路徑搜索input.h文件。

# include 0169d:\ user \ include \ input.h0169

(2)先從目錄中搜索input.h文件。找不到時,再去標準目錄中檢索。

# include 0169input.h0169

(3)從係統設定的標準目錄中搜索。

# include

文件包括語句是很有用的,特別是對包括多個源文件的大程序來說,可以把各個文件中共同使用的函數說明、符號常量定義、外部量說明、宏定義和結構類型定義等寫成一個獨立的包括文件、在需要這些說明的源文件中,隻需在源文件的開頭用一個# include語句把該文件包括進來即可,這樣可避免重複勞動。

在第八章中,我們介紹過在引用輸入/輸出函數時,需要包括一些頭文件(以.h為後綴的文件)。這些頭文件中一般包含輸入/輸出函數所需要的符號常數、宏和函數說明等。

做成包括文件的另一個好處是:當這些常量、宏定義等需要修改時,隻需修改這個包括文件即可,而不必修改各個源文件。

在使用# include語句時,應注意以下兩點:

?一個# include語句隻能包含一個包括文件。如果需要包括n個文件時,就需要n個# include語句。

?文件包括語句可以嵌套。

9.2 宏定義

C編譯係統的預編譯程序提供了宏定義機製,利用這種機製可以定義符號常量和宏。下麵敘述符號常量和宏的定義。

9.2.1 符號常量的定義

符號常量用宏定義語句定義,宏定義語句的一般形式如下:

# define 符號常量名 字符串

該語句把符號常量名定義為指定的字符串。在進行預編譯處理時,用該字符串替代程序中出現的符號常量名。例如:

# define TRUE 1

# define FALSE 0

把TRUE定義為1,FALSE定義為0。在符號常量定義之後,就可以用它來編碼了。例如:

if(i= =TRUE)

printf(0169Congratulations! You are right! \ n0169);

else if(i= =FALSE)

Printf(0169Sorry! You are wrong! \ n0169);

對於該程序段,在進行預編譯時,就把程序中出現的TRUE和FALSE分別用1和0替代,於是就變為:

if(i= =1)

printf(0169Congratulations! You are right! \ n0169);

else if(i= =0)

Printf(0169Sorry! You are wrong! \ n0169);

在符號常量定義語句中,字符串可以是一個數值數據、表達式或字符串。例如:

# define PI 3.14159

# define S (PI * r * r)

# define PRT printf

# define A (20-(3 * 4))

如果字符串是一個運算表達式時,一般應該用括號括住它,以便把它視為一個操作對象與其他操作數進行計算,否則,會由於操作優先級的問題而發生錯誤。例如:

text=A *8

預編譯後,該表達式變為:

text=(20-(3 *4))*2

如果A定義為:

# define A 20-3 * 4

則表達式var=EXPR * 8經預編譯後變為:

var=20-3 *4 *2

這就不符合原意。因此,在宏定義語句中的字符串為一般表達式(而不是一個操作數)時,為了保證正確的運算次序,應該用括號括住它。在程序設計中,使用符號常量有如下兩點好處:

(1)增強程序的可讀性

由於符號常量含義明確,於是采用符號常量書寫的程序要比不用符號常量可讀性更強。例如:

# define LENGTH 40

# define WIDTH 120

# define HEIGTH 80

在程序中使用LENGTH、WIDTH、HEIGTH時,一看就知道它們分別代表長、寬、高,而40、120、80則很難猜出它們是長、寬、高。

(2)增強程序的可維護性

如果一個常量在程序的多處被引用,可把它定義為符號常量。這樣,在以後需改動該常量時,隻需改動它的宏定義語句即可,而不必對每一個引用它的地方進行修改。這不但可以減少改錯的工作量,而且可以避免漏改。

使用符號常量應注意以下幾點:

?符號常量名一般用大寫字母(也可以用小寫字母),以便與其他標識符相區別。符號常量名的命名規則同一般標識符一致。

?宏定義語句不是C語言的語句,因此,不能用分號結尾。如果使用分號結尾,則分號被視為字符串的一部分。例如:

# define AGE 50;

以上格式表示AGE被定義為“50;”而不是50。於是,在預編譯時,程序中凡是出現AGE的地方,都用“50;”替換。這就不符合原意了。

?宏定義語句應放在函數定義之外,符號常量的有效範圍是從定義它的宏定義語句開始至所在源文件的末尾。一般宏定義語句都放在源文件的開頭,以便使它對整個源文件都有效。

?可用# undef預編譯語句結束宏定義的作用域。例如:

# define AGE 30

main()

# undef AGE / * AGE為30的宏定義結束 * /

func()

該符號常量的有效域是到“# undef AGE”語句為止。

?在定義符號常量時,可以引用已定義的符號常量。例如:

# define PI 3.1415926

# define R 10

# define S PI * R * R

main()

printf(0169S=% f0169,S);

預編譯後,該程序變為:

main()

printf(0169S=% f0169,3.1415926 * 10 * 10);

?如果程序中用雙引號括住的字符串內包含與符號常量相同的名字時,預編譯時並不進行替換。例如,上麵的printf()語句中,預編譯時並不替換0169S= % f0169中的S。

9.2.2 帶參數的宏定義

利用# define語句不僅可以定義符號常量,也可以定義帶參數的宏。帶參數的宏的一般定義形式如下:

# define 宏名(參數表) 字符串

例如:

# define min(a,b)(((a)<(b))?(a):(b))

其中min(a,b)是帶參數的宏,a和b是形式參數。該定義把min(a,b)定義為(((a)<(b))?(a):(b))。在定義了該宏之後,就可在程序中用min(a,b)替代定義它的運算表達式(((a)<(b))?(a):(b))。宏的使用方法類似於函數。例如,在需要求兩個數的最小值時,就可使用已定義的宏:

c=min(20,21);

在進行預編譯時,預編譯程序根據宏定義式來替換程序中出現的帶參數宏,其中定義式中的形式參數用相應的實際參數替換。於是,上麵的賦值語句變為:

c=((20<21)?20:21);

在程序設計中,經常要把反複使用的運算表達式定義為帶參數的宏。例如

# define percent(a,b)(1000.0 * (a)/(b)) / *求a是b的千分之幾* /

# define abs(x) ((x)> =0)?(x):-(x)/ / *求x的絕對值 * /

# define max(a,b)(((a)<(b))?(a):(b)) / *求兩個數中較大者* /