一、巨集定義
在c語言源程式中允許用乙個識別符號來表示乙個字串,稱為「巨集
」。被定義為「巨集
」的識別符號稱為「巨集名
」。在編譯預處理時,對程式中所有出現的「巨集名
」,都用巨集定義中的字串去代換,這稱為
「巨集代換」或
「巨集展開」。
巨集定義是由源程式中的巨集定義命令完成的。巨集代換是由預處理程式自動完成的。
在c語言中,
「巨集」分為有引數和無引數兩種。下面分別討論這兩種「巨集」的定義和呼叫。
1.無參巨集定義:
無參巨集的巨集名後不帶引數。
其定義的一般形式為:
#define 識別符號 字串
其中的「#
」表示這是一條預處理命令。凡是以「
#」開頭的均為預處理命令。「
define
」為巨集定義命令。「識別符號」為所定義的巨集名。「字串」可以是常數、表示式、格式串等。
在前面介紹過的符號常量的定義就是一種無參巨集定義。此外,常對程式中反覆使用的表示式進行巨集定義。
例如:#define m (y*y+3*y)
它的作用是指定識別符號m來代替表示式(y*y+3*y)。在編寫源程式時,所有的(y*y+3*y)都可由m代替,而對源程式作編譯時,將先由預處理程式進行巨集代換,即用(y*y+3*y)表示式去置換所有的巨集名m,然後再進行編譯 ,
如:
#define m (y*y+3*y)
main()
上例程式中首先進行巨集定義,定義m來替代表示式(y*y+3*y),在s=3*m+4*m+5* m中作了巨集呼叫。在預處理時經巨集展開後該語句變為:
s=3*(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);
但要注意的是,在巨集定義中表示式(y*y+3*y)兩邊的括號不能少。否則會發生錯誤。如當作以下定義後:
#difine m y*y+3*y
在巨集展開時將得到下述語句:
s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y;
顯然與原題意要求不符。計算結果當然是錯誤的。因此在作巨集定義時必須十分注意。應保證在巨集代換之後不發生錯誤。
對於巨集定義還要說明以下幾點:
1) 巨集定義是用巨集名來表示乙個字串,在巨集展開時又以該字串取代巨集名,這只是一種簡單的代換,字串中可以含任何字元,可以是常數,也可以是表示式,預處理程式對它不作任何檢查。如有錯誤,只能在編譯已被巨集展開後的源程式時發現。
2) 巨集定義不是說明或語句,在行末不必加分號,如加上分號則連分號也一起置換。
3) 巨集定義必須寫在函式之外,其作用域為巨集定義命令起到源程式結束。如要終止其作用域可使用# undef命令。
4) 巨集名在源程式中若用引號括起來,則預處理程式不對其作巨集代換。
5)
巨集定義允許巢狀,在巨集定義的字串中可以使用已經定義的巨集名。在巨集展開時由預處理程式層層代換。
例如:
#define pi 3.1415926
#define s pi*y*y /* pi是已定義的巨集名*/
對語句:
printf("%f",s);
在巨集代換後變為:
printf("%f",3.1415926*y*y);
6) 習慣上巨集名用大寫字母表示,以便於與變數區別。但也允許用小寫字母。
7) 可用巨集定義表示資料型別,使書寫方便。
例如:#define stu struct stu
在程式中可用stu作變數說明:
stu body[5],*p;
#define integer i
在程式中即可用
integer作整型變數說明:
integer a,b ;
應注意用巨集定義表示資料型別和用
typedef定義資料說明符的區別。
巨集定義只是簡單的字串代換,是在預處理完成的,而typedef是在編譯時處理的,它不是作簡單的代換,而是對型別說明符重新命名。被命名的識別符號具有型別定義說明的功能。
8)
對「輸出格式」作巨集定義,可以減少書寫麻煩。
2.帶參巨集定義:
c語言允許巨集帶有引數。在巨集定義中的引數稱為形式引數,在巨集呼叫中的引數稱為實際引數。
對帶引數的巨集,在呼叫中,不僅要巨集展開,而且要用實參去代換形參。
帶參巨集定義的一般形式為:
#define 巨集名(形參表) 字串
在字串中含有各個形參。
帶參巨集呼叫的一般形式為:
巨集名(實參表);
例如:#define m(y) y*y+3*y /*巨集定義*/……
k=m(5); /*巨集呼叫*/……
在巨集呼叫時,用實參5去代替形參y,經預處理巨集展開後的語句為:
k=5*5+3*5
#define max(a,b) (a>b)?a:b
main()
上程式的第一行進行帶參巨集定義,用巨集名max表示條件表示式(a>b)?a:b,形參a,b均出現在條件表示式中。程式第七行max=max(x,y)為巨集呼叫,實參x,y,將代換形參a,b。巨集展開後該語句為:
max=(x>y)?x:y;
用於計算x,y中的大數。
對於帶參的巨集定義有以下問題需要說明:
1)帶參巨集定義中,巨集名和形參表之間不能有空格出現。
例如把:
#define max(a,b) (a>b)?a:b
寫為:
#define max (a,b) (a>b)?a:b
將被認為是無參巨集定義,巨集名max代表字串 (a,b) (a>b)?a:b。巨集展開時,巨集呼叫語句:
max=max(x,y);
將變為:
max=(a,b)(a>b)?a:b(x,y);
這顯然是錯誤的。
2)在帶參巨集定義中,形式引數不分配記憶體單元,因此不必作型別定義。而巨集呼叫中的實參有具體的值。要用它們去代換形參,因此必須作型別說明。這是與函式中的情況不同的。在函式中,形參和實參是兩個不同的量,各有自己的作用域,呼叫時要把實參值賦予形參,進行
「值傳遞」。而在帶參巨集中,只是符號代換,不存在值傳遞的問題。
3)在巨集定義中的形參是識別符號,而巨集呼叫中的實參可以是表示式。
4)在巨集定義中,字串內的形參通常要用括號括起來以避免出錯。在上例中的巨集定義中(y)*(y)表示式的y都用括號括起來,因此結果是正確的。如果去掉括號,把程式改為以下形式:
#define sq(y) y*y
main()
執行結果為:
input a number:3
sq=7
同樣輸入3,但結果卻是不一樣的。問題在**呢? 這是由於代換只作符號代換而不作其它處理而造成的。巨集代換後將得到以下語句:
sq=a+1*a+1;
由於a為3故sq的值為7。這顯然與題意相違,因此引數兩邊的括號是不能少的。即使在引數兩邊加括號還是不夠的,請看下面程式:
#define sq(y) (y)*(y)
main()
本程式與前例相比,只把巨集呼叫語句改為:
sq=160/sq(a+1);
執行本程式如輸入值仍為3時,希望結果為10。但實際執行的結果如下:
input a number:3
sq=160
為什麼會得這樣的結果呢?分析巨集呼叫語句,在巨集代換之後變為:
sq=160/(a+1)*(a+1);
a為3時,由於「/
」和「*
」運算子優先順序和結合性相同,則先作160/(3+1)得40,再作40*(3+1)最後得160。為了得到正確答案應在巨集定義中的整個字串外加括號,程式修改如下:
#define sq(y) ((y)*(y))
main()
以上討論說明,對於巨集定義不僅應在引數兩側加括號,也應在整個字串外加括號。
5) 帶參的巨集和帶參函式很相似,但有本質上的不同,除上面已談到的各點外,把同一表示式用函式處理與用巨集處理兩者的結果有可能是不同的
6) 巨集定義也可用來定義多個語句,在巨集呼叫時,把這些語句又代換到源程式內。
檔案包含是c預處理程式的另乙個重要功能。
檔案包含命令列的一般形式為:
#include"檔名"
在前面我們已多次用此命令包含過庫函式的標頭檔案。例如:
#include"stdio.h"
#include"math.h"
檔案包含命令的功能是把指定的檔案插入該命令列位置取代該命令列,從而把指定的檔案和當前的源程式檔案連成乙個原始檔。
在程式設計中,檔案包含是很有用的。乙個大的程式可以分為多個模組,由多個程式設計師分別程式設計。有些公用的符號常量或巨集定義等可單獨組成乙個檔案,在其它檔案的開頭用包含命令包含該檔案即可使用。這樣,可避免在每個檔案開頭都去書寫那些公用量,從而節省時間,並減少出錯。
對檔案包含命令還要說明以下幾點:
1)包含命令中的檔名可以用雙引號括起來,也可以用尖括號括起來。例如以下寫法都是允許的:
#include"stdio.h"
#include
但是這兩種形式是有區別的:使用尖括號表示在包含檔案目錄中去查詢(包含目錄是由使用者在設定環境時設定的),而不在源檔案目錄去查詢;
使用雙引號則表示首先在當前的源檔案目錄中查詢,若未找到才到包含目錄中去查詢。使用者程式設計時可根據自己檔案所在的目錄來選擇某一種命令形式。
2)乙個include命令只能指定乙個被包含檔案,若有多個檔案要包含,則需用多個include命令。
3)檔案包含允許巢狀,即在乙個被包含的檔案中又可以包含另乙個檔案。
C語言 預處理命令
我們可以在c源程式中插入傳給編譯程式的各中指令,這些指令被稱為預處理器指令,它們擴充了程式設計的環境。現把常用的預處理命令總結如下 1.預處理程式 按照ansi標準的定義,預處理程式應該處理以下指令 if ifdef ifndef else elif endif define undef line ...
C語言預處理命令
以 開頭的預處理命令。如 include,巨集定義命令 define pi 3.1415926等。在源程式中這些命令都放在函式之外,而且一般放在原始檔前面,它們稱為預處理部分。無參巨集定義 無參巨集的巨集名後不帶引數。其定義的一般形式為 define 識別符號 字串 其中的 表示這是一條預處理命令,...
C語言預處理命令
我們經常使用 include命令。使用庫函式之前,應該用 include引入對應的標頭檔案。這種以 號開頭的命令稱為預處理命令。1 編譯 compile 會將原始檔 c檔案 轉換為目標檔案。對於 vc vs,目標檔案字尾為.obj 對於gcc,目標檔案字尾為.o。編譯是針對單個原始檔的,一次編譯操作...