程序的链接和装载(2)--链接概述

    为了能够更好地理解计算机程序中编译和链接的过程中,我们简单地回顾一下计算机程序开发的历史。在最开始的时候,计算机程序的开发并没有如此庞大复杂的自动化编译链接构建工具来帮忙,那个时代的程序员,往往是先把程序写在卡带上,再由计算机读取卡带中的点阵来执行程序。所以当时并没有现如今功能如此强大的编程语言,而是使用机器语言进行编程。

    假设我们的计算机,每条指令占1个字节8bit,而有一种跳转指令,高4位为0001,表示该指令为跳转指令。低4位表示指令跳转的绝对地址。那么从如下机器码中我们可知,地址0上的指令为一条跳转指令,跳转到地址4的位置上。

    0  0001 0100

    1  ...

    2  ...

    3  ...

    4  1000 0111

    5  ...

    那么现在就有一个很棘手的问题了,因为我们都知道程序并不是一成不变的,它可能经常要被修改,所以一旦我们要在地址0~4中插入删除其他指令时,那么原本地址4上的指令就不在地址4中了,这样为了保证程序执行正确,地址0上的跳转指令就要根据目标指令的地址实时修改了。当我们的程序越来越庞大,逻辑越来越复杂时,每当插入或者删除指令时,都有大量的类似跳转指令的指令需要实施修改,这种人工绑定的方法基本是不现实的。

    因此,我们的先驱程序员发明了汇编语言,汇编语言相对于机器语言来说,很大的一个进步在于,使用符号来标记指令的位置。例如,上述的地址4的指令我们对其用符号"Dst"来标记后,不管"Dst"前后插入或者删除了多少指令,我们的汇编器每次再汇编的时候会重新计算"Dst"的位置,并把其他指令中引用"Dst"的部分都替换成真实地址,那么程序就可以正确执行。

    然而随着我们软件规模的日渐庞大,我们程序的代码量也不断膨胀,渐渐地,把所有程序代码按指令一口气从头也到尾也变得不现实了。于是人们开始考虑把不同功能的代码以一定的方式组织起来,一方面更加容易阅读理解,一方面更加有助于模块抽象和模块复用。例如在C语言中,我们用一个个函数来表达一段段子功能,并将包含一类功能相关函数的源文件来表示一个模块集合。那么如何将这些模块进行组合最后形成一个单一的可执行程序呢?这便是模块间的通信问题了。

    C语言中模块之间的通信有两种方式,一种是函数的调用,另一种是变量的引用访问。简单说,其实就是我调用某个函数或者访问某个变量时,我需要知道他们的起始地址,所以这两种方式本身都可以归结为同一种行为,那便是符号的引用。对C语言而言,在链接过程的语境下,符号指的就是函数名和全局变量名。

    类似于拼图,模块间利用符号的引用来通信。定义符号的模块相当于凸出一块的拼图,而引用该符号的模块相当于凹进来的拼图,两个拼图因为契合的那部分而拼接在一起,模块也是因为符号的定义和引用组合在一起。


    所以,链接所做的工作就是把每个模块之间相互引用的部分都处理好,使得各个模块之间能够正确的链接在一起。本质上,链接器所做的工作和我们前面所描述的"先驱程序员们人工调整地址"没什么区别,只是面对现代软件中如此庞大复杂的各种模块,更好的方法是每个模块独立进行编译,再将经过编译后的目标文件组装成一个可执行程序。

    因此,我们知道链接的单元模块为目标文件(.o或者.obj),静态库文件(.a或者.lib),动态库文件(.so或者.dll),下一篇博文中,我们先介绍一些所谓的.o目标文件,究竟有什么内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值