预处理指令由一系列preprocessing tokens组成,其中第一个preprocessing tokens是#(前后都可以有 white space),最后一个preprocessing tokens是此#后的第一个换行符(也就是说一条预处理指令在一个完整的逻辑行)。
Example:
# /
include /
<stdio.h>
等价于:# include <stdio.h>
预处理指令中出现的white-space characters 只能是space and horizontal-tab (包括在翻译阶段3中将注释替换成的空格).
条件包含(翻译)
if-group:
# if constant-expression new-line groupopt
# ifdef identifier new-line groupopt
# ifndef identifier new-line groupopt
elif-groups:
elif-group
elif-groups elif-group
elif-group:
# elif constant-expression new-line groupopt
else-group:
# else new-line groupopt
endif-line:
# endif new-line
控制条件包含的表达式应该是一个整数常量表达式(不能包含强制转换),可以包含
defined identifier
defined (identifier)。
在所有宏替换结束后,每一个preprocessing tokens都应在词法上构成一个token。
源文件包含
#include指令标识一个头文件或源文件。在此指令出现的地方替换整个头文件或源文件。
#include <h-char-sequence> new-line
#include "h-char-sequence" new-line
后者在搜索失败的情况下等同于前者。
Example:
#if VERSION == 1
#define INCFILE "vers1.h"
#elif VERSION == 2
#define INCFILE "vers2.h" /* and so on */
#else
#define INCFILE "versN.h"
#endif
#include INCFILE
宏替换
两类宏:object-like macro, function-like macro
当使用#define重复定义已定义的宏(宏函数),其替换表(宏函数还包括参数表)要完全一致,否则该程序是ill-formed。(在VC下将会对该信息加以警告,gcc则不予理会)。
宏替换的顺序:深度优先和广度优先
下面这个例子用以说明宏替换的顺序问题:
Example:
#define ROOT AAA CCC
#define AAA ABC
#define AAA3 AA
#define BBB AAA3##ROOT AAA3
#define DD(BBB) #BBB
BBB
a=DD(BBB)
VC 和gcc的输出结果是相同的:
AAA3ROOT AA
a="BBB"
这说明两者均是使用基于广度优先的替换规则。
#操作符
#操作符只能够出现于宏函数的替换表中,其后所出现的preprocessing token必须是一个(宏函数)参数。它的作用是在替换表中将 #arg 转换成对应的字符串文字量。
Example:
#define str(s) { b = #s}
str(Hello)
将被替换成
{ b = "Hello"}
##操作符
##操作符不能出现于宏(函数)替换表的开端和末尾(因为要连结)。它的作用是在替换表中将其前后的参数连结成为一个preprocessing token。
Example:
#define concat(s,t) s##t
#define AAA ABC
concat(A,AA)
将被替换成
ABC
重新扫描和替换
在替换列表中的所有参数替换过之后,预处理器将对结果token序列重新扫描以便对其中的宏再次替换。
当正在替换的宏在其替换列表中发现自身时,就对其不再进行替换。进而,在任何正在嵌套替换的宏的替换过程中遇到正被替换的宏就对其不再进行替换(防止递归)。
Example:
#define ROOT AAA CCC
#define AAA ROOT
ROOT
将被替换成
ROOT CCC
宏定义的作用域
宏定义的作用域从其定义处开始知道遇到相应的#undef指令,或者直到编译单元结束。
行控制(#line)
预处理指令:
# line digit-sequence new-line
告知编译器应该把此后的源程序行视作开始于digit-sequence指定的行号。
而:
# line digit-sequence "s-char-sequenceopt" new-line
与之类似,只是当前的文件由s-char-sequence所指定。
Error指令
该指令主要用于指示编译器打印诊断信息。
Pragma指令
该指令主要用于指示编译器完成一些特定功能(implementation-defined)。例如:
#pragma optimize用于优化
#pragma pack 用于指定结构或联合中的成员地址的对齐方式(单位)
#pragma comment 用于向目标文件或可执行文件插入注释,等等。
标准内嵌宏
C++标准定义了6个内嵌宏:
__LINE__ 当前源文件的行号(十进制整数常量)
__FILE__ 当前源文件名(字符串常量)
__DATE__ 编译该源文件的日期(以"Mm dd yyyy"表示的字符串)
__TIME__ 编译该源文件的时间 (以"hh:mm:ss"表示的字符串)
__STDC__ 是否定义及其值都是implementation-defined。
__cplusplus 当编译一个C++翻译单元时,其值被定义为199711L
Example:
我们可以定义这样一个宏:
#define jd printf("***%s***Line:%d", __FILE__,__LINE__)
那么源文件:
#define jd printf("***%s***Line:%d", __FILE__,__LINE__)
int
main()
{
jd;
printf("hello,world/n");
jd;
}
将被替换成:
#line 1 "macro2.cpp"
int
main()
{
printf("***%s***Line:%d", "macro2.cpp",6);
printf("hello,world/n");
printf("***%s***Line:%d", "macro2.cpp",8);
}
Example:
# /
include /
<stdio.h>
等价于:# include <stdio.h>
预处理指令中出现的white-space characters 只能是space and horizontal-tab (包括在翻译阶段3中将注释替换成的空格).
条件包含(翻译)
if-group:
# if constant-expression new-line groupopt
# ifdef identifier new-line groupopt
# ifndef identifier new-line groupopt
elif-groups:
elif-group
elif-groups elif-group
elif-group:
# elif constant-expression new-line groupopt
else-group:
# else new-line groupopt
endif-line:
# endif new-line
控制条件包含的表达式应该是一个整数常量表达式(不能包含强制转换),可以包含
defined identifier
defined (identifier)。
在所有宏替换结束后,每一个preprocessing tokens都应在词法上构成一个token。
源文件包含
#include指令标识一个头文件或源文件。在此指令出现的地方替换整个头文件或源文件。
#include <h-char-sequence> new-line
#include "h-char-sequence" new-line
后者在搜索失败的情况下等同于前者。
Example:
#if VERSION == 1
#define INCFILE "vers1.h"
#elif VERSION == 2
#define INCFILE "vers2.h" /* and so on */
#else
#define INCFILE "versN.h"
#endif
#include INCFILE
宏替换
两类宏:object-like macro, function-like macro
当使用#define重复定义已定义的宏(宏函数),其替换表(宏函数还包括参数表)要完全一致,否则该程序是ill-formed。(在VC下将会对该信息加以警告,gcc则不予理会)。
宏替换的顺序:深度优先和广度优先
下面这个例子用以说明宏替换的顺序问题:
Example:
#define ROOT AAA CCC
#define AAA ABC
#define AAA3 AA
#define BBB AAA3##ROOT AAA3
#define DD(BBB) #BBB
BBB
a=DD(BBB)
VC 和gcc的输出结果是相同的:
AAA3ROOT AA
a="BBB"
这说明两者均是使用基于广度优先的替换规则。
#操作符
#操作符只能够出现于宏函数的替换表中,其后所出现的preprocessing token必须是一个(宏函数)参数。它的作用是在替换表中将 #arg 转换成对应的字符串文字量。
Example:
#define str(s) { b = #s}
str(Hello)
将被替换成
{ b = "Hello"}
##操作符
##操作符不能出现于宏(函数)替换表的开端和末尾(因为要连结)。它的作用是在替换表中将其前后的参数连结成为一个preprocessing token。
Example:
#define concat(s,t) s##t
#define AAA ABC
concat(A,AA)
将被替换成
ABC
重新扫描和替换
在替换列表中的所有参数替换过之后,预处理器将对结果token序列重新扫描以便对其中的宏再次替换。
当正在替换的宏在其替换列表中发现自身时,就对其不再进行替换。进而,在任何正在嵌套替换的宏的替换过程中遇到正被替换的宏就对其不再进行替换(防止递归)。
Example:
#define ROOT AAA CCC
#define AAA ROOT
ROOT
将被替换成
ROOT CCC
宏定义的作用域
宏定义的作用域从其定义处开始知道遇到相应的#undef指令,或者直到编译单元结束。
行控制(#line)
预处理指令:
# line digit-sequence new-line
告知编译器应该把此后的源程序行视作开始于digit-sequence指定的行号。
而:
# line digit-sequence "s-char-sequenceopt" new-line
与之类似,只是当前的文件由s-char-sequence所指定。
Error指令
该指令主要用于指示编译器打印诊断信息。
Pragma指令
该指令主要用于指示编译器完成一些特定功能(implementation-defined)。例如:
#pragma optimize用于优化
#pragma pack 用于指定结构或联合中的成员地址的对齐方式(单位)
#pragma comment 用于向目标文件或可执行文件插入注释,等等。
标准内嵌宏
C++标准定义了6个内嵌宏:
__LINE__ 当前源文件的行号(十进制整数常量)
__FILE__ 当前源文件名(字符串常量)
__DATE__ 编译该源文件的日期(以"Mm dd yyyy"表示的字符串)
__TIME__ 编译该源文件的时间 (以"hh:mm:ss"表示的字符串)
__STDC__ 是否定义及其值都是implementation-defined。
__cplusplus 当编译一个C++翻译单元时,其值被定义为199711L
Example:
我们可以定义这样一个宏:
#define jd printf("***%s***Line:%d", __FILE__,__LINE__)
那么源文件:
#define jd printf("***%s***Line:%d", __FILE__,__LINE__)
int
main()
{
jd;
printf("hello,world/n");
jd;
}
将被替换成:
#line 1 "macro2.cpp"
int
main()
{
printf("***%s***Line:%d", "macro2.cpp",6);
printf("hello,world/n");
printf("***%s***Line:%d", "macro2.cpp",8);
}