0. 涉及编译原理
源文件由编译器编译为目标文件,目标文件再由链接器链接为可执行程序
内存分为代码区,数据区,堆区,栈区
可执行程序保存在磁盘中的,在启动时会将代码移至内存中的代码区。
1.库
1.1 什么是库
库即为源代码的二进制文件
试想你开发了一个功能但不希望使用者修改源码的同时能使用该源代码,就可以通过将源代码编译为二进制并打包起来,这就是库,是一种可执行代码的二进制形式,可以视为是对源代码的包装和保护,同时要提供一个头文件里写清你的函数的声明、参数、返回值...要将该头文件和库一起发给使用者,使用者通过头文件里你的描述使用库中的功能。这也是为什么函数声明要放在头文件里的原因之一。
许多功能我们没必要重复造轮子,通过库我们能复用自己或别人成熟的代码
由于windows和linux的本质不同,因此二者库的二进制是不兼容的。
1.2 库的种类
在windows中 在linux中
.dll 动态库 .so 动态库
.lib 静态库 .a 静态库
即使是很简单源文件,在编译的时候若引用了标准库也需要重新编译一遍标准库内的所有代码
这个过程非常浪费时间。因此为解决重复编译问题可以提前把标准库编译出来
综上,为加快编译速度和包装源代码的两个原因
静态编译用的静态库和动态编译用的动态库的都是机械码文件,只是在加载方式上有很大区别
1.2.1 静态库
生成方式:
首先将源文件编译成目标文件:gcc –c a.c b.c
生成静态库:ar –rc libstatic.a a.o b.o
如何生成静态库/动态库文件
导入静态库
- 将得到的.lib静态库文件和.h头文件粘贴到项目文件夹内,.lib头文件名和.h头文件名可能不同,.lib静态库文件名是项目名,.h头文件名是在项目内创建的。
- 在项目内(的头文件虚拟文件夹)添加.h头文件,此时还需要在项目设置中设置包括目录和库目录,这是为了让编译器和链接器知道去哪里找头文件和库文件,具体细节参照VS中设置目录与依赖项选项-优快云博客
- 在主函数上方导入头文件和静态库,头文件作用是声明函数,.lib则是定义函数,此时程序就可以正常运行了。
#include "xxx.h"//导入头文件 #pragma comment(lib, "xxx.lib")//导入静态库
1.2.2 动态库(共享库)
生成方式:
同静态库一样编译成目标文件:gcc –c a.c b.c
生成共享库:gcc –fPIC –shared –o libshared.so a.o b.o
导入动态库:
将需要添加的.dll文件拖拽到项目生成的.exe所在的文件夹下即可(项目->属性->配置属性->常规->输出目录,可以看到.exe生成在哪个目录下)。
1.2.3 标准库与非标准库
如果你写的源文件非常受欢迎那么就是标准库,反之只是你个人用的非标准库,其用途都是为了不用重新造轮子。区别是...是否被人熟知...
2.链接的种类
2.1 静态链接
需要.h(头文件).lib(静态库文件)
可执行程序包含源代码和使用的库,将所有代码打包到可执行程序的链接方式叫静态链接。
优缺点
库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中。即有n份静态链接的程序就会有n份标准库在磁盘和内存中。这就会导致最终生成的可执行代码量相对变多,相当于编译器将代码补充完整了,这样运行起来相对就快些。
不过会有个缺点: 占用磁盘和内存空间. 静态库会被添加到和它连接的每个程序中, 而且这些程序运行时, 都会被加载到内存中. 无形中又多消耗了更多的内存空间
2.2 动态链接
需要.h(头文件).lib(导入库文件,又称函数引用表).dll(动态库文件)
lib是导入库文件,只是和静态库使用了相同的扩展名,执行代码位于动态库中,需要导入库提供地址符号等信息,静态链接和动态链接的lib文件大小差距很大。
可执行程序中只包含我们写的代码而不再包含库文件,取而代之的是一份声明,告知程序某处有这个功能的导入库文件(导入库/共享库文件),将链接的时机推迟到了运行时
优缺点
静态链接: 动态链接:
可执行文件只包含它需要的函数的引用表,而不是所有的函数代码,只有在程序执行时, 那些需要的函数代码才被拷贝到内存中。每个进程都是独立的虚拟内存。通过页面映射把同一份标准库映射到各个进程的虚拟内存空间 这样当多个项目调用同一个库时,内存包括磁盘只需要有一份标准库即可,这样就使可执行文件比较小, 节省磁盘空间。
更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用,也同时节约了内存。
不过由于运行时要去链接库会花费一定的时间,执行速度(指编译到运行需要的时间)相对会慢一些
2.3 总结
注:动态链接只能使用动态库,静态链接只能使用静态库,因此相互可以指代
静态库是牺牲了空间效率,换取了时间效率,动态库是牺牲了时间效率换取了空间效率,没有好与坏的区别,只看具体需要了。
另外,一个程序编好后,有时需要做一些修改和优化,如果我们要修改的刚好是库函数的话,在接口不变的前提下:
使用动态库的程序只需要将动态库重新编译就可以了,
而使用静态库的程序则需要将静态库重新编译好后,将程序再重新编译一遍。