C預處理器
此條目需要補充更多來源。 (2017年3月20日) |
C預處理器是C語言、C++語言的預處理器。用於在編譯器處理程序之前預掃描原始碼,完成頭文件的包含,巨集擴展,條件編譯,行控制(line control)等操作。
編譯階段
C語言標準規定,預處理是指前4個編譯階段(phases of translation)。
- 三字符組與雙字符組的替換
- 行拼接(Line splicing): 把物理源碼行(Physical source line)中的換行符轉義字符處理為普通的換行符,從而把源程序處理為邏輯行的順序集合。
- 單詞化(Tokenization): 處理每行的空白、註釋等,使每行成為token的順序集。
- 擴展巨集與預處理指令(directive)處理。
包含文件
用於包含另一個文件:
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
條件編譯
if-else指令包括#if
, #ifdef
, #ifndef
, #else
, #elif
and #endif
.
#if VERBOSE >= 2
print("trace message");
#endif
#ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */
# include <unistd.h>
#elif defined _WIN32 /* _WIN32 is usually defined by compilers targeting 32 or 64 bit Windows systems */
# include <windows.h>
#endif
#if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined _WIN64
// we are compiling for a 32-bit system
#else
// we are compiling for a 64-bit system
#endif
巨集定義與擴展
有兩種巨集:
- 類似對象的巨集(無參數的巨集)
- 類似函數的巨集(帶參數的巨集),在第一個標識符與左括號之間,絕不能有空格。
#define <identifier> <replacement token list> // object-like macro
#define <identifier>(<parameter list>) <replacement token list> // function-like macro, note parameters
宏定義可以用#undef
取消:
#undef <identifier> // delete the macro
特殊巨集與指令
__FILE__
與 __LINE__
, 擴展為當前文件與行號。例如:
// debugging macros so we can pin down message origin at a glance
#define WHERESTR "[file %s, line %d]: "
#define WHEREARG __FILE__, __LINE__
#define DEBUGPRINT2(...) fprintf(stderr, __VA_ARGS__)
#define DEBUGPRINT(_fmt, ...) DEBUGPRINT2(WHERESTR _fmt, WHEREARG, __VA_ARGS__)
//...
DEBUGPRINT("hey, x=%d\n", x);
C或C++語言標準定義了巨集: __STDC__
, __STDC_VERSION__
, __cplusplus
,__DATE__
,__TIME__
,__func__
等。
Token字符串化
#
運算符(Stringification Operator)把隨後的token轉化為C語言的字符串。
#define str(s) #s
str(p = "foo\n";) // outputs "p = \"foo\\n\";"
str(\n) // outputs "\n"
即使#運算符後面的是另一個巨集名,這個宏名將不會被巨集展開,而是按照字面值被當作一個字符串。因此,如果需要#運算符後面的巨集名做巨集展開,需要使用兩層巨集的嵌套使用,其中外層的巨集展開時也一併把#運算符後面的巨集名做巨集展開。例如:
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo) // outputs "foo"
xstr (foo) // outputs "4"
Token連接
##
運算符(Token Pasting Operator)連接兩個token為一個token.
#define DECLARE_STRUCT_TYPE(name) typedef struct name##_s name##_t
DECLARE_STRUCT_TYPE(g_object); // Outputs: typedef struct g_object_s g_object_t;
##
運算符左側或右側如果是另一個巨集名,這個宏名將不會被巨集展開,而是按照字面值被當作一個token。因此,如果需要##運算符左右的巨集名做宏展開,需要使用兩層巨集的嵌套使用,其中外層的巨集展開時也一併把##運算符左右的巨集名做宏展開。
用戶定義的編譯錯誤與警告
#error "error message"
#warning "warning message"
編譯器相關的預處理特性
#pragma
指令提供了編譯器特定的預處理功能。
參考文獻
外部連結
維基教科書中的相關電子教學:C Programming/Preprocessor
- ISO/IEC 9899(頁面存檔備份,存於互聯網檔案館). The official C standard. As of 2014, the latest publicly available version is a working paper for C11(頁面存檔備份,存於互聯網檔案館).
- GNU CPP online manual(頁面存檔備份,存於互聯網檔案館)
- Visual Studio .NET preprocessor reference(頁面存檔備份,存於互聯網檔案館)
- Pre-defined C/C++ Compiler Macros project(頁面存檔備份,存於互聯網檔案館): lists "various pre-defined compiler macros that can be used to identify standards, compilers, operating systems, hardware architectures, and even basic run-time libraries at compile-time"