C++提供的編譯預處理功能主要有以下三種:
(一) 宏定義
(二) 文件包含
(三) 條件編譯
預編譯又稱爲預處理 , 是做些代碼文本的替換工作。處理 # 開頭的指令 , 比如拷貝 #include 包含的文件代碼, #define 宏定義的替換 , 條件編譯等,就是爲編譯做的預備工作的階段,主要處理#開始的預編譯指令,預編譯指令指示了在程序正式編譯前就由編譯器進行的操作,可以放在程序中的任何位置。
預處理指令是以#號開頭的代碼行。#號必須是該行除了任何空白字符外的第一個字符。#後是指令關鍵字,在關鍵字和#號之間允許存在任意個數的空白字符。整行語句構成了一條預處理指令,該指令將在編譯器進行編譯之前對源代碼做某些轉換。下面是部分預處理指令:
指令用途
指令 用途
# 空指令,無任何效果
#include 包含一個源代碼文件
#define 定義宏
#undef 取消已定義的宏
#if 如果給定條件爲真,則編譯下面代碼
#ifdef 如果宏已經定義,則編譯下面代碼
#ifndef 如果宏沒有定義,則編譯下面代碼
#elif 如果前面的#if給定條件不爲真,當前條件爲真,則編譯下面代碼
#endif 結束一個#if……#else條件編譯塊
#error 停止編譯並顯示錯誤信息
1.宏
C++ 宏定義將一個標識符定義爲一個字符串,源程序中的該標識符均以指定的字符串來代替。因此預處理命令後通常不加分號。這並不是說所有的預處理命令後都不能有分號出現。由於宏定義只是用宏名對一個字符串進行簡單的替換,因此如果在宏定義命令後加了分號,將會連同分號一起進行置換
#define預處理指令是用來定義宏的。該指令最簡單的格式是:首先神明一個標識符,然後給出這個標識符代表的代碼。在後面的源代碼中,就用這些代碼來替代該標識符。這種宏把程序中要用到的一些全局值提取出來,賦給一些記憶標識符。
#define MIN(A, B) ((A) <= (B)? (A):(B))
用#define定義函數注意點:
將函數中的參數擴起來
#define ADD(x, y) x + y
#define MUL(x, y) x * y
int main ( int argc, char *argv[] )
{
int a= 2, b = 3;
printf ("\nProgram %d\n\n", MUL(ADD(a, b), 5) ); //被轉換成2 + 3 * 5 注意 沒有括號 故結果爲17
return 0;
}
用#define來定義函數的優缺點:
優點:可完成函數調用的功能,又能減少系統開銷,提高運行效率。因爲它是在預處理階段即進行了宏展開,在執行時不需要轉換,即在當地執行。
缺點:所佔用的目標代碼空間相對較大
犧牲空間來換取時間
#define具有替代的作用,可以當做是全局變量的使用,在C++中,把一個變量設置成const後,值也是不會再改變,那麼#define和const有什麼區別的呢?
const常量與define宏定義的區別
(1) 編譯器處理方式不同。define宏是在預處理階段展開,生命週期止於編譯期。
只是一個常數、一個命令中的參數,沒有實際的存在。#define常量存在於程序的代碼段。const常量是編譯運行階段使用,const常量存在於程序的數據段.
(2)類型和安全檢查不同。define宏沒有類型,不做任何類型檢查,僅僅是展開。
const常量有具體的類型,在編譯階段會執行類型檢查。
(3) 存儲方式不同。define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。
const常量會在內存中分配(可以是堆中也可以是棧中)
2.文件包含
#include預處理指令的作用是在指令處展開被包含的文件。包含可以是多重的,也就是說一個被包含的文件中還可以包含其他文件。
在程序中包含頭文件有兩種格式:
#include <my.h>
#include "my.h"
第一種方法是用尖括號把頭文件括起來。這種格式告訴預處理程序在編譯器自帶的或外部庫的頭文件中搜索被包含的頭文件。第二種方法是用雙引號把頭文件括起來。這種格式告訴預處理程序在當前被編譯的應用程序的源代碼文件中搜索被包含的頭文件,如果找不到,再搜索編譯器自帶的頭文件。
採用兩種不同包含格式的理由在於,編譯器是安裝在公共子目錄下的,而被編譯的應用程序是在它們自己的私有子目錄下的。一個應用程序既包含編譯器提供的公共頭文件,也包含自定義的私有頭文件。採用兩種不同的包含格式使得編譯器能夠在很多頭文件中區別出一組公共的頭文件
//總之, #include <my.h>是從標準庫路徑中開始搜索文件, #include "my.h"是編譯器從用戶的工作路徑開始搜索文件
3.條件編譯指令
條件編譯指令將決定那些代碼被編譯,而哪些是不被編譯的。可以根據表達式的值或者某個特定的宏是否被定義來確定編譯條件。
1.#if指令
#if指令檢測跟在製造另關鍵字後的常量表達式。如果表達式爲真,則編譯後面的代碼,直到出現#else、#elif或#endif爲止;否則就不編譯。
2.#endif指令
#endif用於終止#if預處理指令。
#define DEBUG 0 //定義了一個宏
main()
{
#if DEBUG //判斷DEBUG的值,雖然已經定義,但是值是0
printf("Debugging\n");
#endif //DEBUG 爲0,endif成立
printf("Running\n");
}
//由於程序定義DEBUG宏代表0,所以#if條件爲假,不編譯後面的代碼直到#endif,所以程序直接輸出Running。如果去掉#define語句,效果是一樣的。
3.#ifdef和#ifndef
#define DEBUG //定義了一個宏
main()
{
#ifdef DEBUG //判斷DEBUG是否定義,#ifdef是定義了
printf("yes\n");
#endif
#ifndef DEBUG // 判斷DEBUG是否沒定義
printf("no\n");
#endif
}
// #if defined等價於#ifdef; #if !defined等價於#ifndef
4.#else指令
#else指令用於某個#if指令之後,當前面的#if指令的條件不爲真時,就編譯#else後面的代碼。#endif指令將中指上面的條件塊。
#define DEBUG
main()
{
#ifdef DEBUG //是否定義DEBUG
printf("Debugging\n");
#else
printf("Not debugging\n");
#endif
printf("Running\n");
}
//#else,#elif和endif是一樣的
5.#elif指令
#elif預處理指令綜合了#else和#if指令的作用。
#define TWO
main()
{
#ifdef ONE
printf("1\n");
#elif defined TWO
printf("2\n");
#else
printf("3\n");
#endif
}
// 程序很好理解,最後輸出結果是2。
6.其他一些標準指令
#error指令將使編譯器顯示一條錯誤信息,然後停止編譯。
#line指令可以改變編譯器用來指出警告和錯誤信息的文件號和行號。
#pragma指令沒有正式的定義。編譯器可以自定義其用途。典型的用法是禁止或允許某些煩人的警告信息