目录
前言
一、翻译环境和运行环境
二、翻译环境
2.1 预处理
2.1 编译
2.1.1 词法分析
2.1.2 语法分析
2.1.3 语义分析
2.2 汇编
2.3 链接
三、运行环境
四、简答主线问题
前言
本篇主要讨论以下问题:
主线问题:
1. 源文件(.c)如何转换成(.exe)文件并被执行的
补充知识:
2. 源文件(.c)转换成(.exe)文件并被执行的过程中依赖哪两个环境,这两个环境分别做什么事
翻译环境部分:
3. 翻译环境分为哪两个大的过程,这两个过程执行后得到的结果是什么
4. 编译又分为哪三个小的过程,这三个小的过程具体都做了哪些事,得到的结果分别是什么
5. 链接的目的是什么
运行环境部分:
6. 运行环境中大概在做些什么
一、翻译环境和运行环境
1. 源文件(.c)能被转换成(.exe)文件并被执行,依赖着两个环境:翻译环境和运行环境。
2. 翻译环境:实现将源文件中的源代码转换成(.exe)文件中的二进制指令。
3. 运行环境:执行(.exe)文件中的二进制指令。
二、翻译环境
1. 翻译环境是由编译和链接两个大过程组成,这两个过程执行完的结果分别是得到(.obj)目标文件和(.exe)二进制可执行文件。
2. 翻译环境中的编译过程又分为预处理(预编译)、编译、汇编三个小的过程。
3. 编译所用的工具是编译器,链接所用的工具是链接器。
4. 在windows环境下目标文件的后缀是(.obj),Linux环境下目标文件的后缀是(.o)。
5. (老师用的是Linux环境的编译器gcc,来观察的编译和链接过程中的细节)
2.1 预处理
1. gcc test.c -E -o test.i
2. 预处理阶段主要处理源文件中#开头的预编译指令,处理的规则如下:
① 将所有的#define删除,并展开所有的宏定义。
② 处理所有的条件编译指令,比如#if、#ifdef、#elif、#else、#endif等。
③ 处理#include预编译指令,将包含的头文件中的内容拷贝插入预编译指令的位置上,这个过程是递归进行的,即包含的头文件也可能包含着其他的头文件。
④ 删除所有的注释。
⑤ 添加行号和文件名标识,方便后续编译器生成调试信息。
⑥ 或保留#pragma的编译器指令,方便编译器后续使用。
3. 经过预处理后的(.i)文件中不再包含宏定义,因为宏已经被全部展开,所包含的头文件信息都已经插入到(.i)文件中,所以当我们无法知道宏定义或者头文件是否包含正确的时候,可以查看预处理后的(.i)文件。
2.1 编译
1. 编译过程就是将预处理后的⽂件进⾏⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码⽂件。(C语言代码——>汇编代码)
2. gcc -S test.i -o test.s
2.1.1 词法分析
1. 将源代码程序被输⼊扫描器,扫描器的任务就是简单的进⾏词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字⾯量、特殊字符等)。
2.1.2 语法分析
1. 接下来语法分析器,将对扫描产⽣的记号进⾏语法分析,从⽽产⽣语法树。
2.1.3 语义分析
1. 由语义分析器来完成语义分析,即对表达式的语法层⾯分析。编译器所能做的分析是语义的静态分析,静态语义分析通常包括声明和类型的匹配,类型的转换等,这个阶段会报告错误的语法信息。
2.2 汇编
1. 汇编器是将汇编代码转转变成机器可执⾏的指令,每⼀个汇编语句⼏乎都对应⼀条机器指令。汇编指令和机器指令是根据对照表⼀⼀的进⾏翻译的,并不会做指令的优化。
2. gcc -c test.s -o test.o
2.3 链接
1. 链接过程主要包括:地址和空间分配,符号决议和重定位等步骤(符号决议和重定位后,会通过决议后的符号的地址去找相对应的函数)。
2. 链接主要目的是解决⼀个项⽬中多⽂件、多模块之间互相调⽤的问题。
三、运行环境
①将程序载入内存->②调用main函数->③建立运行时堆栈,执行代码->④正常/异常终止
四、简答主线问题
问:源文件(.c)如何转换成(.exe)文件并被执行的?
答:多个(.c)和(.h)文件经过预处理生成多个(.i)文件,(.i)文件经过编译处理生成多个(.s)汇编代码文件,(.s)文件经过汇编处理生成多个(.obj)二进制指令文件,多个(.obj)文件和链接库经过链接器的处理最终生成了一个(.exe)文件,(.exe)文件再在运行环境中经处理实现代码的执行。(<编译和链接>当前了解这么多即可,不需要更深入的了解。如果后期想要深入了解可以看《程序员的自我修养》这一本书。)