翻译阶段
来自 cppreference.com
C 源文件被编译器处理,如同以下阶段按顺序执行。实际实现可以组合这些操作或以不同的方式处理它们,只要行为相同。
目录 |
[编辑] 阶段 1
1) 源代码文件(通常是使用 UTF-8 等多字节编码的文本文件)的各个字节以实现定义的方式映射到源字符集的字符。特别是,操作系统相关的换行符被替换为换行符。
- 源字符集是一个多字节字符集,它包含基本源字符集作为单字节子集,包括以下 96 个字符
a) 5 个空白字符(空格、水平制表符、垂直制表符、换页符、换行符)
b) 从 '0' 到 '9' 的 10 个数字字符
c) 从 'a' 到 'z' 和从 'A' 到 'Z' 的 52 个字母
d) 29 个标点符号:_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " '
2) 三字母序列 被替换为相应的单字符表示形式。(直到 C23)
[编辑] 阶段 2
1) 当反斜杠出现在行尾(紧跟换行符)时,反斜杠和换行符都被删除,将两行物理源代码合并为一行逻辑源代码。这是一个单遍操作:一行以两个反斜杠结尾,后面是一个空行,不会将三行合并为一行。
运行此代码
#include <stdio.h> #define PUTS p\ u\ t\ s /* Line splicing is in phase 2 while macros * are tokenized in phase 3 and expanded in phase 4, * so the above is equivalent to #define PUTS puts */ int main(void) { /* Use line splicing to call puts */ PUT\ S\ ("Output ends here\\ 0Not printed" /* After line splicing, the remaining backslash * escapes the 0, ending the string early. */ ); }
2) 如果非空源文件在执行此步骤后没有以换行符结尾(无论是最初没有换行符,还是以反斜杠结尾),则行为未定义。
[编辑] 阶段 3
1) 源文件被分解为注释、空白字符序列(空格、水平制表符、换行符、垂直制表符和换页符)和预处理标记,它们是以下内容
a) 头文件名称:<stdio.h> 或 "myfile.h"
b) 标识符
e) 运算符和标点符号,如 +、<<=、<% 或 ##。
f) 不属于任何其他类别的单个非空白字符
2) 每个注释都被替换为一个空格字符
3) 换行符保留,非换行空白字符序列是否可以合并为单个空格字符取决于实现。
如果输入已解析为预处理标记直到给定字符,则下一个预处理标记通常被认为是构成预处理标记的最长字符序列,即使这会导致后续分析失败。这通常被称为最大吞噬。
int foo = 1; // int bar = 0xE+foo; // error: invalid preprocessing number 0xE+foo int bar = 0xE/*Comment expands to a space*/+foo; // OK: 0xE + foo int baz = 0xE + foo; // OK: 0xE + foo int pub = bar+++baz; // OK: bar++ + baz int ham = bar++-++baz; // OK: bar++ - ++baz // int qux = bar+++++baz; // error: bar++ ++ +baz, not bar++ + ++baz int qux = bar+++/*Saving comment*/++baz; // OK: 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) // OK: <MACRO_2> is not a header-name
[编辑] 阶段 4
1) 预处理器 被执行。
2) 使用#include 指令引入的每个文件都会递归地经历阶段 1 到阶段 4。
3) 在此阶段结束时,所有预处理器指令都将从源代码中删除。
[编辑] 阶段 5
1) 字符常量 和 字符串字面量 中的所有字符和转义序列 从源字符集转换为执行字符集(它可以是 UTF-8 等多字节字符集,只要基本源字符集中列出的所有 96 个字符在阶段 1 中都有单字节表示)。如果转义序列指定的字符不是执行字符集的成员,则结果取决于实现,但保证不会是空(宽)字符。
注意:此阶段执行的转换可以在一些实现中通过命令行选项控制: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 翻译阶段 (p: 9-10)
- 5.2.1 字符集 (p: 17)
- 6.4 词法元素 (p: 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 翻译阶段 (p: 9-10)
- 5.2.1 字符集 (p: 17-19)
- 6.4 词法元素 (p: 49-66)
- C89/C90 标准 (ISO/IEC 9899:1990)
- 2.1.1.2 翻译阶段
- 2.2.1 字符集
- 3.1 词法元素
[编辑] 另请参阅
C++ 文档 for 翻译阶段
|