Try   HackMD

前置處理器

tags: Competitive Programming Note

本文已停止更新,新版請至 WiwiHo 的競程筆記

大家應該都看過這些東西:

#define test 123 #include<iostream>

這種以 # 為開頭的程式碼稱為前置處理器,大家應該都知道 C++ 的程式碼需要先經過編譯才能執行,而前置處理器是在編譯之前就執行的,經過前置處理器處理完後的程式碼才會被編譯。

舉例來說,#include 會在編譯前將指定檔案裡的文字完全複製貼上到你 include 的那個地方,它可以用在任何地方,例如:

你開了一個檔案叫做 hello.txt,內容只有 "hello",然後你在同目錄有另一個檔案 test.cpp,內容是:

#include<iostream> using namespace std; int main(){ cout << #include "hello.txt" << "\n"; return 0; }

那麼會輸出 hello,在編譯之前,#include "hello.txt" 這一行就會被替換成 "hello"

還有一個常見的前置處理器是 #define,結構是 #define 識別碼(identifier) 替換字串,也被稱為「巨集」或「宏」,英文是 macro,有些人可能會說這是「常數」,然後你就會開始懷疑它跟 const 的差別,其實 #define 並不是在宣告一個常數,而是它會把整份程式碼中的一段特定文字替換,例如:

#include<iosteam> #define hello "hello" using namespace std; int main(){ cout << hello << "\n"; return 0; }

在編譯之前,hello 就會被替換成 "hello",接著才進行編譯,所以輸出會是 hello。它也有函式的用法:

#inlcude<iosteam> #define say(a) cout << a << "\n" using namespace std; int main(){ say("hello"); return 0; }

這樣會輸出 hello,在編譯前,say("hello") 會被替換成 cout << a << "\n"

要特別注意的一點是,因為這是把文字原封不動貼上,所以 #define 替換的部分不會先做運算,而是在執行期按照前置處理器處理完的程式碼運算,例如:

#include<iostream> #define plus(a, b) a + b using namespace std; int main(){ cout << (plus(1, 2) * 3) << "\n"; return 0; }

這樣的結果並不是 9,而是 7,因為替換完後的程式碼會是 1 + 2 * 32 * 3 會先被計算。所以我建議用個括號把它包起來。

以上是 #define 的「巨集」用法,而 #define 還有另一個功用,前置處理器也有 if 的語法,它們都和 #define 有關。

最單純的是 #if#elif#else#endif,用法例如:

#include<iostream> #define A 3 using namespace std; int main(){ #if A == 3 cout << "test\n"; #elif A == 2 cout << "hello\n"; #else cout << "QQ\n"; #endif return 0; }

這就像是一般的 if 控制語法,而是以 #endif 結束區塊,要巢狀結構也可以。如果判斷結果是 true,那個區塊才會被編譯,上述的範例中,只有 A==3 這個判斷會是 true,因此只有第 7 行會被編譯,第 9 和 11 行不會。前置處理器的判斷只能判 #define 定義的東西,幾乎所有運算子都可以用。如果把第 2 行改成 #define A 2,那被編譯的就會是第 9 行,如果 A 不是 2 也不是 3,那被編譯的就會是第 11 行。

除了可以判斷值之外,也可以判斷一個識別碼有無被定義,用 defined(識別碼) 就可以得到指定的識別碼有沒有 define 過,例如:

#include<iostream> #define A using namespace std; int main(){ #if defined(A) cout << "test\n"; #endif #if defined(B) cout << "hi\n"; #endif return 0; }

第 7 行會被編譯,而第 10 行不會。#if defined(A) 可以簡寫為 #ifdef A,而 #if !defined(A) 可以簡寫為 #ifndef A

這通常會用來避免標頭檔被重複 include,至於競程上,如果你打了一大段測試用的程式碼,覺得要在 submit 前註解掉、WA 了又要取消註解很麻煩,那就可以用個 #ifdef 來處理,會方便許多。