个人主页点这里~
本文篇幅较短,但内容在理解c语言内部逻辑方面却十分重要,希望大家能够认真的阅读
编译和链接
一、翻译环境和运行环境二、翻译环境1、预处理2、编译①词法分析②语法分析③语义分析 3、汇编4、链接 三、运行环境
一、翻译环境和运行环境
在c语言的实现中,存在两个环境
翻译环境:在这个环境中源代码被转换成可执行的二进制指令
运行环境:实际执行代码
二、翻译环境
翻译由编译和链接两个过程组成
编译又可以分为预处理、编译、汇编三个过程
一个c语言的项目一般是由多个.c文件一起构建的,多个.c文件通过如下步骤生成可执行程序:
①多个.c文件单独经过编译器,编译处理生成对应的目标文件
②多个目标文件和链接库一起经过链接器处理生成最终的可执行程序
③链接库是指运行时库(支持程序运行的基本函数集合)或者第三方库
注意:在Windows环境下目标文件的后缀是.obj,Linux环境下的目标文件的后缀是.o
以下就是编译与链接的过程:
1、预处理
在预处理阶段,源文件和头文件都会被处理成为.i为后缀的文件
预处理阶段主要处理源文件中#开始的预编译指令,规则如下:
①将所有的#define删除,并展开所有的宏定义
②处理所有的条件编译指令
③处理 #include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,因为包含的头文件可能包含其他文件
④删除所有的注释
⑤添加行号和文件名标识,方便后续编译器生成调试信息
⑥保留所有的 #pragma 的编译器指令,后续编译器会使用
经过预处理后的.i文件中不再包含宏定义,所有的宏定义已经被展开了,我们可以通过查看预处理后的.i文件来确认宏定义和头文件是否包含正确
2、编译
编译的过程就是将预处理后的文件进行语法词法的分析和优化,生成汇编代码
①词法分析
源代码程序会被输入扫描器,在扫描器中被简单词法分析,把代码中的字符分割开
arr[input] = (input+2)*(5-2);
这个代码是一个数组赋值代码,我们进行词法分析的时候会将它们全部拆开:
记号 | 类型 |
---|---|
arr | 标识符 |
[ | 左方括号 |
input | 标识符 |
] | 右标识符 |
= | 赋值 |
( | 左圆括号 |
input | 标识符 |
+ | 加号 |
2 | 数字 |
) | 右圆括号 |
* | 乘号 |
( | 左圆括号 |
5 | 数字 |
- | 减号 |
2 | 数字 |
) | 右圆括号 |
②语法分析
然后进入语法分析器,将分割开的字符记号进行语法分析,产生语法树(以表达式为结点的树)
③语义分析
然后进入语义分析器进行语义分析,也就是对表达式进行语法层面的分析,这个分析是静态分析,编译器能且只能做到静态分析,它通常包括声明和类型的匹配,类型的转换等,这个阶段会报告错误的语法信息
3、汇编
然后接下来一步是进入汇编器,将汇编指令转换为机器可执行的指令,根据汇编指令和机器指令的对照表一一翻译,不做指令优化
4、链接
然后就是链接,链接就是把一堆文件链接在一起生成可执行程序,它的过程主要包括:地址和空间的分配、符号决议、重定位等
重定位:当我们使用多个.c文件生成一个可执行程序的时候,由于编译的时候是每个文件单独编译的,当我们在一个文件中使用另一个文件定义的函数时,我们是需要明确地址的,这时我们会将地址暂时搁置,等到链接的时候再将地址修正,让它们的地址真正成为这个函数的地址,这个地址修正的过程,就叫做重定位
三、运行环境
1、程序必须载入内存当中:程序必须载入内存当中,在有操作系统的环境中,一般由这个操作系统来完成,在独立的环境中,需要手动操作,也可能是通过可执行代码置入只读内存完成
2、程序执行:程序执行便开始调用main函数
3、开始执行程序代码:开始执行程序代码时程序将使用一个运行时堆栈,存储函数的局部变量和返回地址,程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直保留它们的值
4、终止程序:正常终止main函数或意外终止
今日分享就到这里了~