預處理指令
C原始碼在進行編譯前會先經過預處理,預處理指令均以#開頭,結尾沒有分號(;)不是C語句。前處理器採用“語言符號”的分析方式,將空格作為區分標誌之間的符號,一行作為一條指令,“\”可以將預處理指令延伸到下一行。
一、巨集定義
1.不帶引數的巨集定義
(1)不帶引數的巨集定義就是用一個識別符號(巨集名)來代表一個字串。它的一般形式為
#define Macro Str
在預處理的時候程式中的巨集名Macro被替換為字串Str,這個過程稱為巨集展開。
(2)#define指令出現在程式中函式的外面,巨集名的有效範圍為該指令行起到本原始檔結束或#undef。
(3)巨集展開只是簡單的字串替換,簡單巨集常用於定義常量,巨集沒有型別,也沒有優先順序的概念,使用定義常量主要用於指定陣列長度 #define ayyLength 256 ,建議儘量使用const或enum代替巨集定義常量 const int arrLen 256; 。建議不使用巨集定義型別 #define Status int 而是用typedef關鍵字 typedef Status int; 。
(4)巨集不是C語句不能加分號,否則會將分號一起代入。考慮到優先順序問題,其中表示式也可能需要括號。
(5)巨集代替的字串可以是常量也可以表示式,格式描述符,語句(甚至是它們的一部分)等任何C程式中出現的字串。 當然,巨集代替的字串中也可以包含已定義的巨集名。
#undef macro
#undef指令可以終止巨集名的定義。
2.帶引數的巨集定義
(1)
#define Macro(argus) str
對帶引數的巨集展開 是將帶實參的巨集 按照#define指令行中按從左到右的順序進行置換。巨集名和帶引數的括號之間不能加空格,否則將成為無引數巨集,空格後的每一個字元都將作為替代字串的一部分。
(2)帶引數的巨集不是函式,它只是進行簡單字元代換 。定義巨集時引數和字串可以是任意的,但在定義時要注意識別符號不能出現重名【#define Macro(Macro) str】(利用程式碼塊作用域)。在實際呼叫時引數可能是單個資料物件也可能是表示式,由於巨集只進行簡單文字代換考慮到優先順序和結合性的問題,建議在定義巨集時將引數用圓括號括起以作為一個獨立單位。
(3)含參巨集類似於inline函式,其呼叫也是採用傳址的方式(真正的在呼叫點嵌入函式程式碼)。巨集無型別,其引數也沒有型別,只是一個符號代表,含參巨集可以作為模板函式(C++引入,不同型別的引數可以使用同一段函式體)。
(4)巨集與函式相比沒有引數傳遞和返回值的限制使用更加自由靈活,而函式相對獨立便於完成較複雜的任務。函式呼叫需較多時間處理記憶體等,而巨集不需要。
3.巨集定義中的運算子:
(1)對程式作預處理前,編譯器會進行翻譯處理。編譯器首先把原始碼中的字元對映到源字符集。然後編譯器查詢反斜線後緊跟換行符(這裡指按下Enter鍵在原始碼中產生的換行符而非轉義字元'\n')的例項並刪除這些例項。所以反斜線加Enter鍵可以將巨集定義擴充套件到多行。
(2)#運算子與引數結合可以那引數名轉換為相應字串。例如:x是巨集的形參,實參為1時#x將被替換為"1"(字串),x將被替換為1(數值常量)。
(3)##運算子可以把兩個語言符號組合成一個語言符號。例如:n是巨集的形參,實參為1時,x##n將變為識別符號x1,如果無##編譯器將把xn當做一個語言符號,在巨集引數中中無法找到於是不進行替換。
二、檔案包含處理
#include<filename> 或 #include"filename"
- 尖括號中的檔名優先在編譯器安裝目錄中查詢(通常是標準庫),雙引號中的檔案優先在工作目錄中查詢(自定義庫)。
- 檔案包含處理是指將另一個原始檔的全部內容包含進來,即將另外的檔案內容包含到本檔案之中,插入到當前位置,代替預處理指令然後進行編譯得到一個目標檔案。
- 標頭檔案中只有函式宣告和巨集定義,真正的實現在庫中。在連結(linking)時,庫才被連結進來。
- 這種常用在檔案頭部的被包含檔案被稱為標頭檔案(header),常以.h作為字尾。當然不用.h作為字尾用.c作為字尾也是可以的,但是用.h更能表示此檔案的性質。
- 同樣,#include指令不一定要出現在檔案首部。應當注意,被包含檔案修改後,凡包含此檔案的所有檔案都要全部重新編譯。檔案包含處理是將要包含的檔案的內容代替預處理指令,成為原始檔的一部分。
三、條件編譯
條件編譯使得程式中的一部分內容只在滿足一定條件時才進行編譯或不進行編譯。
1.ifdef指令
#ifdef Label Block1 #else Block2 #endif
若指定的識別符號已經被#define指令定義過,則對程式段1進行編譯否則對程式段2進行編譯。可以用於程式除錯等。
也可以用
#ifdef Block #endif
2.ifndef指令
#ifndef Label Block1 #else Block2 #endif
與1正好相反,若識別符號未被定義則編譯程式段1,否則編譯程式段2。
為了避免標頭檔案重複包含,通常使用條件編譯:
#ifndef STDIO_H #define STDIO_H ... #endif
3.if指令
#if expr Block1 #else Block2 #endif
表示式為真時編譯程式段1,否則編譯程式段2。
不用條件編譯指令而用if語句同樣也可以實現,但是那樣做目標程式長(所有語句都參加編譯),執行時間長(在if語句處需進行邏輯判斷)。