Phases of translation
C源文件由编译器进行处理, 如同 以下阶段严格按照此顺序发生。实际实现可以合并这些操作或采用不同处理方式,只要行为保持一致即可。
目录 |
阶段 1
- 源字符集 是一个多字节字符集,它包含作为单字节子集的 基本源字符集 ,该子集由以下96个字符组成:
阶段 2
#include <stdio.h> #define PUTS p\ u\ t\ s /* 行拼接发生在阶段2,而宏 * 在阶段3被词法分析并在阶段4展开, * 因此上述代码等价于 #define PUTS puts */ int main(void) { /* 使用行拼接调用 puts */ PUT\ S\ ("Output ends here\\ 0Not printed" /* 行拼接后,剩余的反斜杠 * 转义了0,导致字符串提前结束 */ ); }
阶段 3
如果输入已解析到给定字符处的前处理令牌,通常会将下一个前处理令牌视为能构成前处理令牌的最长字符序列,即使这会导致后续分析失败。这通常被称为 maximal munch 原则。
int foo = 1; // int bar = 0xE+foo; // 错误:无效的预处理数字 0xE+foo int bar = 0xE/*注释扩展为空格*/+foo; // 正确:0xE + foo int baz = 0xE + foo; // 正确:0xE + foo int pub = bar+++baz; // 正确:bar++ + baz int ham = bar++-++baz; // 正确:bar++ - ++baz // int qux = bar+++++baz; // 错误:bar++ ++ +baz,而非 bar++ + ++baz int qux = bar+++/*分隔注释*/++baz; // 正确:bar++ + ++baz
最大吞食规则唯一的例外是:
- 头文件名预处理记号仅在 #include 或 #embed (C23 起) 指令内、 __has_include 和 __has_embed 表达式内 (C23 起) ,以及在 #pragma 指令的实现定义位置中形成。
#define MACRO_1 1 #define MACRO_2 2 #define MACRO_3 3 #define MACRO_EXPR (MACRO_1 <MACRO_2> MACRO_3) // 正常:<MACRO_2> 不是头文件名
阶段 4
阶段 5
注意:此阶段执行的转换在某些实现中可通过命令行选项控制:gcc 和 clang 使用 - finput - charset 指定源字符集的编码,使用 - fexec - charset 和 - fwide - exec - charset 分别指定执行字符集在字符串字面量和字符常量中的编码 (无编码前缀的情况) (C11 起) 。
阶段 6
相邻的 字符串字面量 会被连接。
阶段 7
编译过程:对标记进行语法和语义分析,并将其作为翻译单元进行翻译。
阶段 8
链接过程:将满足外部引用所需的翻译单元和库组件收集到程序映像中,该映像包含在其执行环境(操作系统)中执行所需的信息。
参考文献
- C23 标准 (ISO/IEC 9899:2024):
-
- 5.1.1.2 翻译阶段 (p: TBD)
-
- 5.2.1 字符集 (p: TBD)
-
- 6.4 词法元素 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018):
-
- 5.1.1.2 翻译阶段 (页: 9-10)
-
- 5.2.1 字符集 (页: 17)
-
- 6.4 词法元素 (页: 41-54)
- C11 标准 (ISO/IEC 9899:2011):
-
- 5.1.1.2 翻译阶段 (p: 10-11)
-
- 5.2.1 字符集 (p: 22-24)
-
- 6.4 词法元素 (p: 57-75)
- C99标准(ISO/IEC 9899:1999):
-
- 5.1.1.2 翻译阶段(页码:9-10)
-
- 5.2.1 字符集(页码:17-19)
-
- 6.4 词法元素(页码:49-66)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 2.1.1.2 翻译阶段
-
- 2.2.1 字符集
-
- 3.1 词法元素
参见
|
C++ documentation
for
Phases of translation
|