命名空间
变体
操作

翻译阶段

来自 cppreference.cn
< c‎ | 语言

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"
c) 预处理数字,包括整数常量浮点常量,但也包括一些无效的记号,例如 1..E+3.foo0JBK
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

最大词法分析规则的唯一例外是

#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,只要阶段 1 中列出的基本源字符集中的所有 96 个字符都有单字节表示)。如果转义序列指定的字符不是执行字符集的成员,则结果是实现定义的,但保证不是空(宽)字符。

注意:在此阶段执行的转换可以通过某些实现中的命令行选项进行控制:gcc 和 clang 使用 -finput-charset 指定源字符集的编码,-fexec-charset-fwide-exec-charset 指定没有编码前缀的字符串字面量和字符常量中执行字符集的编码(C11 起)

[编辑] 阶段 6

相邻的字符串字面量被连接。

[编辑] 阶段 7

编译发生:记号被语法和语义分析,并作为翻译单元进行翻译。

[编辑] 阶段 8

链接发生:翻译单元和满足外部引用所需的库组件被收集到一个程序映像中,该映像包含在执行环境中(操作系统)执行所需的信息。

[编辑] 参考文献

  • C23 标准 (ISO/IEC 9899:2024)
  • 5.1.1.2 翻译阶段 (p: 待定)
  • 5.2.1 字符集 (p: 待定)
  • 6.4 词法元素 (p: 待定)
  • 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++ 文档中的翻译阶段