常用的开发环境如Visual Studio、Delphi,是集成开发环境,在集成开发环境中,将编译链接合并到一起一步完成,其合并到一起的过程称为构建
虽然集成开发环境及编译器提供的默认配置、编译与链接参数提供的功能足以强大,但其隐藏了软件运行背后的机制,可能会产生一些莫名其妙的错误
被隐藏的过程
编译过程可分解为四个步骤:预处理、编译、汇编、链接
预处理
如对一个main.c文件预处理
#-E:仅预处理
gcc -E main.c -o main.i
#或
cpp main.c > main.i
预处理过程中处理源文件中以#开始的预编译指令,规则:
- 将“#define”删除并展开宏定义
- 处理所有条件预处理指令,如“#if”,"#endif","#ifdef","#elif","#else"
- “#include”,将被包含文件插入到预处理位置【递归进行】
- 删除所有注释://、/**/
- 添加行号&文件名标识,用于编译时产生调试用的行号信息及出现错误/警告时的信息显示
- 保留“#pragma”编译器指令
在最后,main.i文件中所有的宏已经展开,后续过程中如果无法判断宏定义/头文件包含是否正确时可以来查看该文件
编译
将main.i文件进行词法分析、语法分析、语义分析、中间代码生成、目标代码生成和优化后产生的汇编代码文件【在编译原理课程中学习】
gcc -S main.i -o main.s
GCC已经把预处理和编译合并为一个步骤,使用叫cc1的程序完成这两步骤。可由源文件直接生成main.s文件:
gcc -S main.c -o main.s
实际上gcc命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译编译程序cc1、汇编器as、链接器ld
汇编
汇编器作用:将汇编代码传为机器可执行的指令,几乎每个汇编语句对应一条机器指令。可调用汇编器as完成:
as main.s -o main.o
#或
gcc -c main.s -o main.o
也可以直接由.c文件生成目标文件:
gcc -c main.c -o main.o
链接
若干变量/函数组成一个模块/一个类一个模块。现代大型软件有许多模块,这些模块相互依赖又相互独立
模块进行拼接的过程就是链接。将一个程序分割成多个模块后,单独进行开发、编译、测试,最终组合形成一个单一的程序
链接主要有两个作用:
- 修改目标文件中信息,对地址做重定位
- 把多个目标文件合并成一个可执行文件
对于静态语言C/C++模块之间组合(通信)的方式:模块间的函数调用、模块间的变量访问。函数调用要知道函数地址,变量访问要知道变量的地址,两者可归结为一种方式:模块间符号的引用,定义一符号的模块多出一块区域,引用该符号的区域缺少这一块区域,拼接时刚好拼接在一起:
静态链接
对于复杂软件,将每个源代码独立进行编译,最终将它们组合起来,这个组装模块的过程就是链接。
链接的主要内容在于将各模块之间相互引用部分处理好,让各模块间正常的衔接。
链接器的主要工作是把一些指令对其他符号地址的引用加以修正。
链接过程主要包括:地址和空间分配、符号决议、重定位
链接过程:将每个模块的.c文件编译成目标文件(.o)文件,将目标文件和库一起链接形成最终可执行文件。
eg:在模块A中定义了函数f,B中需要使用f,那么在调用f的指令的目标地址搁置,在最后链接时让连接器根据引用的符号f去相应的模块A中查找其地址,将B模块中所有引用f的指令修正,让其目标地址为找到的地址
重定位:(变量/函数)地址修正的过程,给程序中每个绝对地址引用的位置打补丁,让其指向正确的地址。每个被修正的地方叫一个重定位入口
符号决议:也称为符号绑定,决议更倾向于静态链接,绑定更倾向于动态链接(使用范围不同)
库:就是一组目标文件的包,即将一些最常用的代码编译成目标文件后打包存放
运行库:支持程序运行的基本函数的集合
来源:CSDN
作者:负婆amara
链接:https://blog.csdn.net/qq_41403559/article/details/104770348