程序员的自我修养 第2章 编译和链接

基本过程

从源文件到可执行文件的过程分解为4个步骤,预处理、编译、汇编、链接。
在这里插入图片描述
预编译
gcc -E hello.c -o hello.i

-E Stop after the preprocessing stage; do not run the compiler proper.
The output is in the form of preprocessed source code, which is
sent to the standard output.

预编译过程主要处理那些源代码文件中以#开始的预编译指令。主要规则估下:

  • 将所有的#define删除,并且展开所有的宏定义
  • 处理所有条件编译指令,比如#if #ifdef #elif #else #endif
  • 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个是递归进行的,也就是说被包含的文件可能还包含其他文件。
  • 删除所有的注释// /* */
  • 添加行号和文件名标识,比如#2 ”hello.c“ 2,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或者警告时能够显示行号。
  • 保留所有的#pragma编译器指令,因为编译器需要使用它们。

编译

gcc -S hello.i -o hello.s

-S Stop after the stage of compilation proper; do not assemble. The output is in the form of an assembler code file for each non-assembler input file specified.
By default, the assembler file name for a source file is made by replacing the suffix .c, .i, etc., with .s. Input files that don’t require compilation are ignored.

编译过程就是把预处理完的文件进行一些列词法分析、语法分析、语义分析记忆优化后生产相应的汇编代码文件。
现在版本的gcc把预编译和编译两个步骤合并成一个,使用一个cc1的程序来完成这两个步骤。
在这里插入图片描述
实际上gcc只是后台程序的包装器,gcc会根据不同的参数要求去调用不用程序,比如预编译编译程序cc1、汇编器as、链接器ld。

汇编

as hello.s -o hello.o
gcc -c hello.s -o hello.o

汇编器就将汇编代码转变成机器可以执行的指令。每一个汇编语句几乎对应一条机器指令。
汇编过程由汇编器as来完成。
经过预编译、编译和汇编直接输出目标文件。

链接
链接通过ld产生。

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ -lc hello.o /usr/lib/x86_64-linux-gnu/crtn.o

原书中使用的是静态链接,这里使用的是动态链接。
链接过程把一大堆文件链接起来,最终生成a.out可执行文件。

编译器做了什么

在这里插入图片描述
编译器就是将高级语言翻译成机器语言的一个工具。
以下面程序为例详细解释下
array[index] = (index+4) * (2+6)
//CompilerExpression.c

词法分析
首先源代码程序输入到扫描器scanner,扫描器只是简单的进行词法分析,运用一个类似有限状态机的算法将源代码的字符序列分割成一系列的记号Token。
一个叫lex的程序可以实现词法扫描,它会按照用户之前描述好的词法规则将输入的字符串分割成一个个的记号。
词法分析产生的记号一般可以分为如下几类:关键字、标识符、字面量和特殊符号。
在这里插入图片描述

语法分析
语法分析就是对由扫描器产生的记号进行语法分析,从而产生语法树。
由语法分析器生成的语法树就是以表达式为节点的树。
有一个叫yacc(Yet Another Compiler Compiler)工具可以实现语法分析。
在这里插入图片描述
语义分析
语义分析是由语义分析器来完成。
语义分析分为静态语义和动态语义。
编译器所能分析的语义是静态语义,在编译期就可以确定的语义。静态语义通常包括声明和类型的匹配。
动态语义是只有在运行期才能确定的语义。

经过语义分析阶段以后,整个语法树的表达式都被标识了类型。如果有些类型需要做隐式转换,语义分析程序会在语法树插入相应的转换节点。
在这里插入图片描述
中间语言生成
源代码优化器往往将整个语法树转换成中间代码,这个代码是语法树的顺序标识,已经非常接近目标代码了。
中间代码有很多类型,最常见的有三地址码和P代码。
三地址码为例,最基本的是 x=y op z
在这里插入图片描述
生成的中间代码如下
在这里插入图片描述
目标代码生成与优化
源代码级优化器产生中间代码标志着下面的过程属于编辑器后端。编译器后端主要包括代码生成器和目标代码优化器。
代码生成器将中间代码转换成目标机器代码,这个过程十分依赖于 目标机器,因为不同改的机器有着不同字长、寄存器、整数数据类型和浮点数据类型等等。

代码生成器会生成如下代码序列。
在这里插入图片描述
最后目标代码优化器会对上述的目标代码进行有阿虎,比如选择合适的寻址方式、使用位移来代替乘法运算、删除多余的指令等等。
在这里插入图片描述

链接器

链接器的是时间发展比较长。
是因为一开始程序设计的时候,就遇到问题。程序写好之后就永远不变化的,可能经常被修改,如果没有链接器,程序员需要人工计算每一个子程序或者跳转的目标地址。当程序修改的时候,这些位置就要重新计算,十分繁琐。这个重新计算各个目标地址的过程就叫重定位。

在现代软件开发过程中,软件的规模很大。需要把程序分割成不同的多个模块,这些模块之间如何组合的问题就是模块之间如何通信的问题。最常见的是C/C++模块之间的通信两种方式,一个是模块间的函数调用,另一个是模块之间的变量访问。这两种方式都需要目标的地址,归根到底是模块间符号的引用。模块之间的拼合的方式就是链接。

静态链接
人为地把每个源代码模块独立地编译,然后将他们组装起来,这个组装模块的过程就是链接。
链接的主要内容就是把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确的衔接。

链接过程主要包括了地址和空间分配、符号决议(符号绑定/名称绑定/名称决议/地址绑定/指令绑定)、重定位等这些步骤。
在这里插入图片描述
每一个模块的源代码文件经过编译器编译成目标文件,目标文件和库一起链接成可执行文件。
最常见的库就是运行时库,它是支持程序运行的基本函数的集合。库其实是一些最常用的代码编译成目标文件后打包存放。

在编译模块时候不知道的地址,在链接过程中重新定位,就是重定位的过程。

-----------完

程序员自我修养:链接,装载与库》是一本由林锐、郭晓东、郑蕾等人合著的计算机技术书籍,在该书中,作者从程序员的视角出发,对链接、装载与库等概念进行了深入的阐述和解析。 在计算机编程中,链接是指将各个源文件中的代码模块组合成一个可执行的程序的过程。链接可以分为静态链接和动态链接两种方式。静态链接是在编译时将所有代码模块合并成一个独立的可执行文件,而动态链接是在运行时根据需要加载相应的代码模块。 装载是指将一个程序从磁盘上加载到内存中准备执行的过程。在装载过程中,操作系统会为程序分配内存空间,并将程序中的各个模块加载到相应的内存地址上。装载过程中还包括解析模块之间的引用关系,以及进行地址重定位等操作。 库是指一组可重用的代码模块,通过链接和装载的方式被程序调用。库可以分为静态库和动态库。静态库是在编译时将库的代码链接到程序中,使程序与库的代码合并为一个可执行文件。动态库则是在运行时通过动态链接的方式加载并调用。 《程序员自我修养:链接,装载与库》对于理解链接、装载和库的原理和机制具有极大的帮助。通过学习这些概念,程序员可以更好地优化代码结构和组织,提高程序的性能和可维护性。同时,了解链接、装载和库的工作原理也对于进行调试和故障排除具有重要意义。 总之,链接、装载与库是计算机编程中的重要概念,对于程序员来说掌握这些知识是非常必要的。《程序员自我修养:链接,装载与库》这本书提供了深入浅出的解释和实例,对于想要学习和掌握这些知识的程序员来说是一本非常有价值的参考书籍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sundaygeek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值