C++ 是一個編譯式語言,也就是程式碼要先經過編譯,變成執行檔才能執行。不過「編譯」的過程其實不光只有編譯這麼簡單,例如這個程式碼:
1  | 
這種以 # 為開頭的程式碼是前置處理器的指令,在編譯之前有一個階段就叫作前置處理,前置處理做的事情就是根據你的指令修改程式碼,然後再拿去編譯。
舉例來說,#include 會在編譯前將指定檔案裡的文字完全複製貼上到你 include 的那個地方,它可以用在任何地方,例如:
你開了一個檔案叫做 hello.txt,內容只有 "hello",然後你在同目錄有另一個檔案 test.cpp,內容是: 1
2
3
4
5
6
7
8
9
using namespace std;
int main(){
    cout <<
        
        << "\n";
    return 0;
}hello,在編譯之前,#include "hello.txt" 這一行就會被替換成 "hello"。
還有一個常見的指令是 #define,寫法是 #define identifier 替換字串,也被稱為「巨集」或「宏」(macro),有些人可能會說這是「常數」,然後你就會開始懷疑它跟 const 的差別,其實 #define 並不是在宣告一個常數,而是它會把整份程式碼中的一段特定文字替換,例如:
1  | 
  | 
在編譯之前,hello 就會被替換成 "hello",接著才進行編譯,所以輸出會是 hello。它也有 function 的用法:
1  | 
  | 
這樣會輸出 hello,在編譯前,say("hello") 會被替換成 cout << a << "\n"。
要特別注意的一點是,因為這是把文字原封不動貼上,所以 #define 替換的部分不會先做運算,而是在執行期按照前置處理器處理完的程式碼運算,例如: 1
2
3
4
5
6
7
8
using namespace std;
int main(){
    cout << (plus(1, 2) * 3) << "\n";
    return 0;
}9,而是 7,因為替換完後的程式碼會是 1 + 2 * 3,2 * 3 會先被計算。所以建議都用個括號把它包起來。
以上是 #define 的「巨集」用法,而 #define 還有另一個功用,前置處理器也有 if 的語法,它們都和 #define 有關。
最單純的是 #if、#elif、#else、#endif,用法例如: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using namespace std;
int main(){
    
    cout << "test\n";
    
    cout << "hello\n";
    
    cout << "QQ\n";
    
    return 0;
}#endif 結束區塊,要巢狀結構也可以。如果判斷結果是 true,那個區塊才會被留下來,否則會被刪掉,上述的範例中,只有 A==3 這個判斷會是 true,因此只有第 7 行會留下來,第 9 和 11 行不會。前置處理器的判斷只能判 #define 定義的東西,可以判等於、比大小等等。如果把第 2 行改成 #define A 2,那被留下的就會是第 9 行,如果 A 不是 2 也不是 3,那被留下的就會是第 11 行。
除了可以判斷值之外,也可以判斷一個 identifier 有無被定義,用 defined(identifier) 就可以得到指定的 identifier 有沒有 define 過,例如: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
using namespace std;
int main(){
    
    cout << "test\n";
    
    
    cout << "hi\n";
    
    return 0;
}#if defined(A) 可以簡寫為 #ifdef A,而 #if !defined(A) 可以簡寫為 #ifndef A。至於已經 define 過的東西可以用 #undef 移除。
這通常會用來避免標頭檔被重複 include,至於競賽用途,如果你打了一大段測試用的程式碼,覺得要在 submit 前註解掉、WA 了又要取消註解很麻煩,那就可以用個 #ifdef 來處理,會方便許多。
其實通常也只會用到 define 而已 (?),define 在競賽中最大的功能就是把常用的東西變得比較好寫,例如這是一個常見的 pair 模板:
1  | 
  | 
除了普通的把東西替換掉之外,function macro 還有一種特別的寫法:
1  | 
  | 
#a 就是把你在 a 打的東西變成字串替換上去,所以 err(num); 會被替換成:
1  | cerr << "num" << ": " << a << "\n";  |