翻译阶段

来自cppreference.com
< cpp‎ | language

编译器如同以此准确顺序发生下列阶段一般处理 C++ 源文件:

目录

[编辑] 阶段 1

1) 以实现定义方式,映射源文件的单独字节,为源字符集的字符。特别是以换行字符替换依赖 OS 的行尾指示符。
源字符集是包含作为单字节子集的基本源字符集的多字节字符集,后者由以下 96 个字符组成:
a) 5 个空白字符(空格、水平制表、垂直制表、换页、换行)
b) 10 个数字字符,从 '0''9'
c) 52 个字母,从 'A''Z' 以及从 'a''z'
d) 29 个标点字符: _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
2) 以其通用字符名(用 \u\U 转义)或某种等价处理的实现定义形式,替换任何不能映射到基本源字符集中的字符。
3) 以对应的单字节表示替换三标符
(C++17 前)

[编辑] 阶段 2

1) 凡在反斜杠出现于行尾(立即为换行符所后随)时,删除反斜杠和换行符,把二个物理源码行组合成一个逻辑源码行。这是单趟操作:以二个反斜杠结束,后随一个空行的行不会把三行组合为一。若于此阶段组成通用字符名( \uXXX ),则行为未定义。
2) 若此步骤后,非空源文件不以换行符结束(无论是原本就无换行,还是以反斜杠结束),则行为未定义 (C++11 前)添加终止的换行符 (C++11 起)

[编辑] 阶段 3

1) 将源文件分解为注释、空白字符(空格、水平制表、换行、垂直制表、换页)序列和下列预处理记号
a) 头文件名: <iostream>"myfile.h"
c) 预处理数字
d) 字符字符串字面量,包含用户定义 (C++11 起)
e) 运算符与标点(包括替用记号),例如 +<<=<%##and
f) 不属于任何其他类别的单独非空白字符
2) 恢复任何在阶段 1 和 2 期间,任何无处理字符串字面量的首尾双引号间进行的变换。
(C++11 起)
3) 以一个空格字符替换每段注释。

保持换行符。是否可将非换行的空白符序列缩减成单个空格字符是未指定的。

若已经分析输入为到给定字符为止的预处理记号,则通常将能构成一个预处理记号的最长字符序列处理成下个预处理记号,即这会导致后继分析失败。这常被称为最大吞噬 (maximal munch)

int foo = 1;
int bar = 0xE+foo;   // 错误:非法的预处理数字 0xE+foo
int baz = 0xE + foo; // OK
 
int quux = bar+++++baz; // 错误: bar++ ++ +baz ,而非 bar++ + ++baz 。

最大吞噬规则的单独例外是:

  • 若下个字符所开始的字符序列能为生字符串字面量的前缀和起始双引号,则下个预处理记号应当为生字符串字面量。该字面量由匹配生字符串模式的最短字符序列组成。
#define R "x"
const char* s = R"y"; // 病式的生字符串字面量,非 "x" "y"
const char* s2 = R"(a)" "b)"; // 生字符串字面量后随普通字符串字面量
  • 若下三个字符是 <:: ,而后继字符既非 : 亦非 > ,则把 < 自身当做预处理记号(而非替用记号 <: 的首字符)。
struct Foo { static const int v = 1; };
std::vector<::Foo> x; // OK : <: 不被用作 [ 的替用记号
extern int y<::>;     // OK :同 extern int y[]; 。
int z<:::Foo::value:>; // OK : int z[::Foo::value];
(C++11 起)
  • 头文件名预处理记号仅在 #include 指令中形成。
std::vector<int> x; // OK : <int> 不是头文件名

[编辑] 阶段 4

1) 执行预处理器
2) #include 指令所引入的每个文件都经历阶段 1 到 4 ,递归执行。
3) 此阶段结束时,从源码移除所有预处理器指令。

[编辑] 阶段 5

1)字符字面量字符串字面量中的所有字符及转义序列源字符集转换成执行字符集(可为如 UTF-8 的多字节字符集,只要来自阶段 1 中所列的基本源字符集的所有 96 个字符拥有单字节表示)。若转义序列所指定的字符不是执行字符集的成员,则结果是实现定义的,但保证不是空(宽)字符。

注意:某些实现中,能以命令行选项控制此阶段所进行的转换: gcc 和 clang 用 -finput-charset 指定源字符集的编码,用 -fexec-charset-fwide-exec-charset 指定无编码前缀的 (C11 起)字符串字面量和字符字面量中的执行字符集的编码,而 Visual Studio 2015 Update 2 及之后版本分别用 /source-charset/execution-charset 指定源字符集和执行字符集。

[编辑] 阶段 6

连接相邻的字符串字面量

[编辑] 阶段 7

发生编译:按照语法和语义分析记号,并将它们翻译成翻译单元。

[编辑] 阶段 8

检验每个翻译单元,产生要求的模板实例化列表,包含显式实例化所要求者。定位模板定义,并进行要求的实例化,以产生实例化单元

[编辑] 阶段 9

将翻译单元、实例化单元和满足外部引用所需的库组件到汇集成程序映像,它含有在其执行环境(操作系统)中执行所需的信息。

[编辑] 注意

某些编译器不实现实例化单元(又称为模板仓库模板注册表),而是简单地在阶段 7 编译每个模板实例化,存储代码于其所显式或隐式要求的对象文件中,然后链接器于阶段 9 将这些编译后的实例化缩减到一个。

[编辑] 引用

  • C++11 standard (ISO/IEC 14882:2011):
  • 2.2 Phases of translation [lex.phases]
  • C++98 standard (ISO/IEC 14882:1998):
  • 2.1 Phases of translation [lex.phases]

[编辑] 参阅

翻译阶段C 文档