编译和链接——程序员的自我修养

半城伤御伤魂 提交于 2020-03-10 16:28:05

常用的开发环境如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的指令修正,让其目标地址为找到的地址

重定位:(变量/函数)地址修正的过程,给程序中每个绝对地址引用的位置打补丁,让其指向正确的地址。每个被修正的地方叫一个重定位入口

符号决议:也称为符号绑定,决议更倾向于静态链接,绑定更倾向于动态链接(使用范围不同)

库:就是一组目标文件的包,即将一些最常用的代码编译成目标文件后打包存放
运行库:支持程序运行的基本函数的集合

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!