Bootstrap

(C语言) 8大翻译阶段

(C语言) 8大翻译阶段

⭐前言

我们常说的C语言编译的4阶段,预处理,汇编,编译,链接

其实这背后有复杂和细分的阶段,将之称为翻译阶段。

而C语言共有8个这样的阶段。

  1. 字符映射
  2. 行分割
  3. 标记化
  4. 预处理
  5. 字符集映射
  6. 字符串拼接
  7. 翻译
  8. 链接

🗃️8大阶段

🗂️1. 字符映射

编译器将物理源文件中的字符转换为编译器可以理解的内部表示。

这通常涉及到字符编码的处理,比如将文件中的UTF-8UTF-16等编码字符统一转换为编译器可识别的源字符集字符集。

注意:在 C23 前需要处理三标符

源字符集是包含作为单字节子集的基本源字符集的多字节字符集,后者由以下 96 个字符组成:

a) 5 个空白字符(空格、水平制表、垂直制表、换页、换行)
b) 10 个数字字符,从 ‘0’ 到 ‘9’
c) 52 个字母,从 ‘A’ 到 ‘Z’ 以及从 ‘a’ 到 ‘z’
d) 29 个标点字符: _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’

🗂️2. 行分割

编译器将物理行转化为逻辑行。

将代码并按行分割,这样每行可以单独进行处理。

在源代码中由反斜杠\结尾(紧跟着换行符),删除反斜杠和换行符号,将下一个物理行连接上来合并为一个逻辑行。

注意:若此步骤后,非空源文件不以换行符结束(无论是原本就无换行,还是以反斜杠结束),则行为未定义。

物理行 (physical line) -> 逻辑行 (logical line)

物理文件

#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,提早结束了字符串。
               */
);
}

效果

#include <stdio.h>

#define PUTS puts

int main(void)
{
                                  PUTS

 ("Output ends here\0Not printed"



);
}

🗂️3. 标记化

编译器将源代码分解最小独立单元。

将源文件分解为注释、空白字符(空格、水平制表、换行、垂直制表、换页)序列和下列预处理记号

​ a) 头文件名:<stdio.h>"myfile.h"

​ b) 标识符

​ c) 预处理数字,包括整数常量浮点常量,但也包括一些非法记号,例如 1…E+3.foo 或 0JBK

​ d) 字符常量字符串字面量

​ e) 运算符与标点,例如 +、<<=、<% 或 ##。

​ f) 不属于任何其他类别的单独非空白字符

以一个空格字符替换每段注释

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

另一种分类法:

Tokens:

The smallest individual units are known as tokens such as keywords, identifiers, strings, operators &

special symbols.

  1. Keywords are the reserved (special) words and can’t be used as variable, functions names.
  2. Identifiers refer to the names of the variables, arrays, functions, classes etc.
  3. Constants refer to fixed values that we cannot change in a program.
  4. String is a group of characters.
  5. Operators are special symbols which operate on variable & constants and form expressions.
  6. Special symbols are () {} [] etc.

🗂️4. 预处理

预处理器对上述的结果进行预处理。

执行预处理器

#include 指令所引入的每个文件都经历阶段 1 到 4(上述所有阶段),递归执行。

此阶段结束时,从源码移除所有预处理器指令。

🗂️5. 字符集映射

将源字符集转换成执行字符集。

字符常量字符串字面量中的所有字符及转义序列源字符集转换成执行字符集(可为如 UTF-8 的多字节字符集,只要来自阶段 1 中所列的基本源字符集的所有 96 个字符拥有单字节表示)。

注意:若转义序列所指定的字符不是执行字符集的成员,则结果是实现定义的,但保证不是空(宽)字符。

🗂️6. 字符串拼接

连接相邻的字符串字面量

例如,使用字符串化运算符(#)可以将两个字符串合并。

#include <stdio.h>

int main() {
    const char* str = "Hello" ", " "World";
    printf(str);
}

最后将输出:Hello, World

🗂️7. 翻译

对每个翻译单元进行翻译。

发生编译:对各个记号进行语法和语义分析,并将它们作为翻译单元完成翻译。

这个阶段将预处理后的源代码转换成中间表示形式,然后进一步转换成目标代码。

这包括语法分析、语义分析、中间代码生成、代码优化和目标代码生成。

🗂️8. 链接

将所有需要的二进制文件连接成一个可执行程序。

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

🗃️C++ 的 9 大阶段

C++ 共 9 个阶段,本质和 C语言 差不多,有一些细节的差异。

最核心的是在翻译和链接之间,有一个实例化模板阶段。

🗂️实例化模板

检验每个翻译单元,产生所要求的模板实例化的列表,其中包括显式实例化所要求的实例化。

定位模板定义,并进行所要求的实例化,以产生实例化单元




⭐END

🌟ref

C 翻译阶段 - cppreference.com

C++ 翻译阶段 - cppreference.com

Effective C 中文版 (豆瓣)

🌟交流方式

关注我,学习更多C/C++,python,算法,软件工程,计算机知识!

⭐交流方式⭐ |C/C++|算法|设计模式|软件架构-CSDN社区

B站

👨‍💻主页:天赐细莲 bilibili

;