Linux:动态链接与静态链接以及动态库与静态库
引言:
我准备用三个月聚焦Linux学习,从系统编译到网络编译,让我们对Linux有全新的认识
前一章博客的问题这一章就会得到解答,所以接下来的博客就敬请期待
动态链接和静态链接
静态链接:
在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数,但是每个源⽂件都是独⽴编译的,即每个*.c⽂件会形成⼀个*.o⽂件,为了满⾜前⾯说的依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序。这个链接的过程就是静态链接。
静态链接的缺点很明显:
• 浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对
同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有
printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;
• 更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程
序。
静态链接的优点:
在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快
动态链接:
动态链接的出现解决了静态链接中提到问题。动态链接的基本思想是把程序按照模块拆分成各个相对独⽴部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件。
动态链接其实远⽐静态链接要常⽤得多。⽐如我们查看下 code 这个可执⾏程序依赖的动态库,会发现它就⽤到了⼀个stdc++动态链接库:

一般我们看名字是将前面的lib和后面的.*去掉
在这⾥涉及到⼀个重要的概念: 库
• 我们的cpp程序中,并没有定义“cout”的函数实现,且在预编译中包含的“iostream”中也只有该
函数的声明,⽽没有定义函数的实现,那么,是在哪⾥实“cout”函数的呢?
• 最后的答案是:系统把这些函数实现都被做到名为 libstdc++.so.6 的库⽂件中去了,在没有特别指定
时,gcc 会到系统默认的搜索路径“/usr/lib”下进⾏查找,也就是链接到 libstdc++.so.6 库函数中去,这样
就能实现函数“cout”了,⽽这也就是链接的作⽤
静态库和动态库
• 静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运
⾏时也就不再需要库⽂件了。其后缀名⼀般为“.a”
• 动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由
运⾏时链接⽂件加载库,这样可以节省系统的开销。动态库⼀般后缀名为“.so”,如前⾯所述的
libstdc++.so.6 就是动态库。g++ 在编译时默认使⽤动态库。完成了链接之后,g++ 就可以⽣成可执⾏⽂件,如下所⽰
• gcc默认⽣成的⼆进制程序,是动态链接的,这点可以通过 file 命令验证(file后面会简绍)
📌 注意1:
• Linux下,动态库XXX.so, 静态库XXX.a
• Windows下,动态库XXX.dll, 静态库XXX.lib
📌 注意2:
⼀般我们的云服务器,C/C++的静态库并没有安装,可以采⽤如下⽅法安装

安装完成后,可以来看看动态链接和静态链接的内存大小

高了百倍,因为静态链接就是拷贝了一份需要的库,而动态链接只需要记录动态库的地址,到时候去查找就行
用file可以查看这个可执行程序是什么链接方式

| 对比维度 | 静态库(.a) | 动态库(.so) |
|---|---|---|
| 编译阶段 | 编译时直接嵌入可执行文件 | 编译时仅记录库地址,运行时加载 |
| 文件大小 | 较大(包含库代码副本) | 较小(仅存链接信息) |
| 运行依赖 | 不依赖系统库,可独立运行 | 依赖系统存在对应动态库 |
| 运行速度 | 更快(无需加载外部库) | 稍慢(运行时动态加载) |
| 更新维护 | 库更新需重新编译程序 | 库更新无需重新编译程序 |
| 内存占用 | 多个程序共用库时,内存多副本 | 多个程序共用库时,内存单副本 |
其他问题:
• 条件编译应⽤场景
分割代码,比如你买一个编译器,有专业版有免费版,免费版就是把专业版的功能剪切下来的,为了实现这个功能,有两种办法,一种就是写很多个不同的代码对应不同的情况,但是维护起来十分麻烦,因为有很多份代码,更新也麻烦,也徒增工作量,也有一种方法是条件编译,既可以只使用一份代码使用条件编辑剪切不同份,维护成本更新成本都相对很低
• 为什么⾮得把语⾔变成汇编
这是因为历史问题,最先出现有编译器的是汇编语言,汇编语言编译器会将汇编语言转化为二进制语言让计算机做出指令,既然有了这个编译器,那以后的语言只需要将自己的语言编译为汇编语言就可以了,降本增效,我们无需成为巨人,我们可以站在巨人的肩膀上
• 编译器⾃举
用你发明的语言写的编译器在汇编编译器上跑一下,生成出的新的文件就是你的编译器汇编代码,这样相当于你发明的语言在用你语言写的编译器上汇编为汇编语言,实现功能完成闭环,这就是自举
