11 前處理指令 前處理指令可以要求前處理器 (preprocessor) 在程式編譯之前, 先進行加入其它檔案的內容 文字取代以及選擇性編譯等工作 1/39 前處理指令 11.1 11.2 11.3 11.4 11.5 前處理器使用 #define 進行文字取代使用 #define 設定巨集指令條件式編譯其他與編譯器有關的前處理指令 2/39
3/39 前處理指令 #include 前處理指令不是 C++ 敘述, 不用分號 ; 做結尾 例如 : #include <iomanip> 與 #include 配合的檔案名稱有兩種語法 例如 : #include Commom.h" 附錄 C 中列出了 C++ 標準程式庫裏面常用的標檔 可以將檔案的詳細路徑寫在雙引號內 例如 : #include c:\c++test\commom.h" 使用 #define 進行文字取代 以 #define 開頭的前處理指令有四種功能 : 1. 宣告某一個代號的值 2. 產生符號常數 (symbolic constant) 3. 產生巨集 (macro) 4. 宣告某一個代號已經被設定 4/39
以 #define 開頭的前處理指令 #define 代號被取代文字 除了包括在字串 (string) 之內的文字以外, 所有吻合的文字, 都將被代號直接取代 例如 : #define and && #define or #define not! 5/39 範例程式 LogicTest.cpp // 前處理指令 #define and && #define or #define not! #include <iostream> using namespace std; 6/39
// ------ 主程式 --------------------------- int main() { float t1,t2; t1=0.5; t2=t1*0.2; cout << "((t1 > 0.8) and (t2 <0.9) or (t1>0.2))= " << ((t1 > 0.8) and (t2 <0.9) or (t1>0.2)) << endl; cout << " not(t1 > t2) = " << not(t1 > t2) << endl; return 0; 7/39 程式執行結果 ((t1> 0.8) and (t2 <0.9) or (t1>0.2))= 1 not(t1 > t2) = 0 8/39
宣告符號常數 (symbolic constant) 符號常數因為直接取代程式內的代號, 本身並不佔用記憶空間, 而每個常數在定義時即擁有自己的記憶空間 #define RAND_MAX 0x7FFFU #define PI 3.1415926 比較好的做法是使用常數 例如 : const double PI = 3.1415926; const double PI = 2.0*asin(1.0); 9/39 前處理指令內禁用的代號 代號 -- LINE -- -- FILE -- DATE -- -- -- TIME -- 意義行號檔名編譯日期編譯時間 10/39
以 #line 開頭的前處理指令 行號不會加在程式裹面, 程式的檔案名稱也沒有真的被更動, 這個指令的最大用途在於大型檔案的除錯 (debugging) #line 120 #line 120 "Test File.cpp" 11/39 範例程式 Reserved.cpp 使用保留代號 // Reserved.cpp #include <iostream> using namespace std; // ---- 主程式 ---------------------------- int main() { cout << " 行號 : " << --LINE-- << endl; cout << " 檔名 : " << --FILE-- << endl; cout << " 編譯日期 : " << --DATE-- << endl; cout << " 編譯時間 : " << --TIME-- << endl; 12/39
#line 120 "Test File.cpp" cout << " 執行 #line 120 \"Test File.cpp\" 之後 : " << endl; cout << " 行號 : " << --LINE-- << endl; cout << " 檔名 : " << --FILE-- << endl; return 0; 13/39 程式執行結果 行號 : 7 檔名 : Reserved.cpp 編譯日期 : Sep 13 2004 編譯時間 : 09:30:38 執行 #line 120 "Test File.cpp" 之後 : 行號 : 122 檔名 : Test File.cpp 14/39
使用 #define 設定巨集 (macro) 巨集指令的參數直接取代巨集本體部份的代號 例如 : #define MAX(x,y) ((x) > (y)? (x) : (y)) 它的功能相當於下列的 inline 樣版函數 : 15/39 template <class T> inline const T& MAX(const T& x, const T& y) {return x > y? x : y; 巨集本體部份的代號在執行時會被引數直接取代 如果設定了一個叫做 PRODUCT 的巨集 : #define PRODUCT(x,y) (x * y) 則 double F1 = 12.5, F2 = 8.38; cout << PRODUCT(F1+2, F2+6) << endl; 相當於執行 : double F1 = 12.5, F2 = 8.38; cout << 12.5 + 2 * 8.38 + 6 << endl; 16/39
PRODUCT 巨集和 inline 樣版函數 #define PRODUCT(x,y) ((x) * (y)) 它的功能相當於下列的 inline 樣版函數 : template <class T> inline const T& PRODUCT(const T& x, const T& y) {return x*y; 17/39 將巨集指令的參數名稱直接以字串的型式輸出 只要將巨集本體部份相對於參數的代號前加上 # 即可 例如 : #define ShowValue(x) \ { \ cerr << #x << " 的值是 : " << x << end \ 如果我們在程式中執行下列敘述 : double F1= 12.5; ShowValue(F1); 結果 : F1 的值是 : 12.5 18/39
19/39 合併巨集指令的參數 例如, 定義一個名叫 Merge 的巨集 : #define Merge(x, y) x##y 如果在程式中執行下列敘述 : double F1= 12.5; cout << " 執行 Merge(F,1) 的結果是 : " << Merge(F,1) << endl; 可以得到下列結果 : 執行 Merge(F,1) 的結果是 : 12.5 範例程式 MacroTest.cpp: 建立和使用 MAX PRODUCT Merge, 和 ShowValue 四個巨集指令 20/39 // MacroTest.cpp #include <iostream> using namespace std; // -- 巨集指令 --------------------------- #define MAX(x,y) ((x) > (y)? (x) : (y)) #define PRODUCT(x,y) ((x) * (y)) #define Merge(x, y) x##y #define ShowValue(x) \ { \ cerr << #x << " 的值是 : " << x << endl; \ cerr << " 按 Enter 鍵繼續." << endl; \ cin.get(); \
21/39 // ---- 主程式 ------------------------------- int main() { int a = 3, b = 9; double F1= 12.5, F2=8.38; cout << "MAX(a, b) = " << MAX(a, b) << endl; cout << "MAX(F1, F2) = " << MAX(F1, F2) << endl; cout << "PRODUCT(F1, F2) = " << PRODUCT(F1+2, F2+6) << endl; ShowValue(F1); cout << "\n 執行 Merge(F,2) 的結果是 :" << Merge(F,2) << endl; return 0; 程式執行結果 MAX(a, b) = 9 MAX(F1, F2) = 12.5 PRODUCT(F1, F2) = 208.51 F1 的值是 : 12.5 按 Enter 鍵繼續. 執行 Merge(F,2) 的結果是 : 8.38 22/39
條件式編譯 (conditional compilation) 可以有選擇性地執行某些前處理指令, 或有選擇性地只編譯原始程式碼的某些部份 常用的有下列數種 : #ifdef #ifndef #else #endif #if defined() #if!defined() #elif 23/39 使用 #define 設定代號 下列指令設定了代號 Mark: #define Mark 24/39
以前處理指令避免重複加入同一個標頭檔 (1) 25/39 例如 : 在標頭檔 Commom.h 內加入 : #ifndef Common_h #define Common_h // 標頭檔 Common.h 的主要內容 // 如果 Common_h 沒設定過才加入這個部份 #endif 在上式中, #ifndef Common_h 也可以寫成 #if!defined(common_h) 以前處理指令避免重複加入同一個標頭檔 (2) 標頭檔 Commom.h 還可以寫成下列格式 : #ifdef Common_h #else #define Common_h // 標頭檔 Common.h 的主要內容 // 如果 Common_h 沒設定過才加入這個部份 26/39 #endif
範例程式 Common.h 檔案 MacroTest.cpp 分成 Common.h 和 Macro2.cpp 兩個檔案 // Common.h #ifndef Common_h #define Common_h #include <iostream> using namespace std; 27/39 // -- 巨集指令 --------------------------- #define MAX(x,y) ((x) > (y)? (x) : (y)) #define PRODUCT(x,y) ((x) * (y)) #define Merge(x, y) x##y #define ShowValue(x) \ { \ cerr << #x << " 的值是 : " << x << endl; \ cerr << " 按 Enter 鍵繼續." << endl; \ cin.get(); \ #endif 28/39
範例程式 Macro2.cpp // MacroTest.cpp // Macro2.cpp #include "Common.h" // ---- 主程式 -------------------- int main() { int a = 3, b = 9; double F1= 12.5, F2=8.38; 29/39 cout << "MAX(a, b) = " << MAX(a, b)<< endl; cout << "MAX(F1, F2) = " << MAX(F1, F2) << endl; cout << "PRODUCT(F1, F2) = " << PRODUCT(F1+2, F2+6) << endl; ShowValue(F1); cout << "\n 執行 Merge(F,2) 的結果是 : " << Merge(F,2) << endl; return 0; 30/39
利用前處理指令插入除錯專用的敘述 藉由 #define 和 #undef 前處理指令來做為執行的開關 例如, 將巨集 Breakpoint 以下列的方式包裹起來 : #define Debug #if defined(debug) #define Breakpoint(x) { \ cerr << #x << " 的值是 : " << x << endl; \ cerr << " 按 Enter 鍵繼續." << endl; \ cin.get(); \ #else #define Breakpoint(x) #endif 31/39 利用前處理指令暫時忽略部份程式 藉由 #if 0 和 #endif 把要暫時忽略不編譯的區域包圍起來 如下列語法所示 : // ---- 其它敘述 ---- #if 0 // ---- 暫時不處理的區域 #endif // ---- 其它敘述 ---- 32/39
利用 assert( ) 協助除錯 標頭檔 <cassert> 提供了函數 assert() 可以使用 #define NDEBUG #undef NDEBUG 做為函數 assert() 的開關 assert(i<15); 一旦 I 大於或等於 15, 就會立即關閉程式, 並發出錯誤訊息 : Assertion failed: line 40 i < 15, file Test.cpp, 33/39 範例程式 Debug.cpp // Debug.cpp #define Debug #if defined(debug) #define Breakpoint(x) \ { \ cerr << #x << 的值是 : << x << endl; \ cerr << 按 Enter 鍵繼續. << endl; \ cin.get(); \ #else #define Breakpoint(x) #endif #undef NDEBUG 34/39
#include <iomanip> #include <iostream> #include <stdlib> #include <cassert> using namespace std; inline double Rand() // 定義 inline 函數 Rand() {return double(rand())/rand_max; 35/39 36/39 // ---- 主程式 ----------------------------- int main() { srand(int(time(0))); const int Size = 20; double Data[Size]; for (int i=0; i<size; i++) { Data[i]= 10.0*Rand()-5.0; assert(i<size); Breakpoint(Data[Size-1]); return 0;
程式執行結果 ( 因為程式會產生亂數, 每一次執行結果都不相同 ) Data[Size-1] 的值是 : 1.72658 按 Enter 鍵繼續 37/39 #pragma 前處理指令 有某些特殊的前處理指令可以被某些編譯器接受, 例如 #pragma warn -nak 38/39
#error 前處理指令 下列前處理指令 : #ifndef cplusplus #error Must use C++ for the type bcd. #endif 這個語法相當於下列指令 : #ifndef cplusplus cerr << Must use C++ for the type bcd. ; #endif 39/39