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"
|