Competitive Programming Note
本文已停止更新,新版請至 WiwiHo 的競程筆記
大家應該都看過這些東西:
這種以 #
為開頭的程式碼稱為前置處理器,大家應該都知道 C++ 的程式碼需要先經過編譯才能執行,而前置處理器是在編譯之前就執行的,經過前置處理器處理完後的程式碼才會被編譯。
舉例來說,#include
會在編譯前將指定檔案裡的文字完全複製貼上到你 include 的那個地方,它可以用在任何地方,例如:
你開了一個檔案叫做 hello.txt
,內容只有 "hello"
,然後你在同目錄有另一個檔案 test.cpp
,內容是:
那麼會輸出 hello
,在編譯之前,#include "hello.txt"
這一行就會被替換成 "hello"
。
還有一個常見的前置處理器是 #define
,結構是 #define 識別碼(identifier) 替換字串
,也被稱為「巨集」或「宏」,英文是 macro,有些人可能會說這是「常數」,然後你就會開始懷疑它跟 const
的差別,其實 #define
並不是在宣告一個常數,而是它會把整份程式碼中的一段特定文字替換,例如:
在編譯之前,hello
就會被替換成 "hello"
,接著才進行編譯,所以輸出會是 hello
。它也有函式的用法:
這樣會輸出 hello
,在編譯前,say("hello")
會被替換成 cout << a << "\n"
。
要特別注意的一點是,因為這是把文字原封不動貼上,所以 #define
替換的部分不會先做運算,而是在執行期按照前置處理器處理完的程式碼運算,例如:
這樣的結果並不是 9
,而是 7
,因為替換完後的程式碼會是 1 + 2 * 3
,2 * 3
會先被計算。所以我建議用個括號把它包起來。
以上是 #define
的「巨集」用法,而 #define
還有另一個功用,前置處理器也有 if 的語法,它們都和 #define
有關。
最單純的是 #if
、#elif
、#else
、#endif
,用法例如:
這就像是一般的 if 控制語法,而是以 #endif
結束區塊,要巢狀結構也可以。如果判斷結果是 true
,那個區塊才會被編譯,上述的範例中,只有 A==3
這個判斷會是 true
,因此只有第 7 行會被編譯,第 9 和 11 行不會。前置處理器的判斷只能判 #define
定義的東西,幾乎所有運算子都可以用。如果把第 2 行改成 #define A 2
,那被編譯的就會是第 9 行,如果 A
不是 2
也不是 3
,那被編譯的就會是第 11 行。
除了可以判斷值之外,也可以判斷一個識別碼有無被定義,用 defined(識別碼)
就可以得到指定的識別碼有沒有 define 過,例如:
第 7 行會被編譯,而第 10 行不會。#if defined(A)
可以簡寫為 #ifdef A
,而 #if !defined(A)
可以簡寫為 #ifndef A
。
這通常會用來避免標頭檔被重複 include,至於競程上,如果你打了一大段測試用的程式碼,覺得要在 submit 前註解掉、WA 了又要取消註解很麻煩,那就可以用個 #ifdef
來處理,會方便許多。