Linux下
静态库:把实现的代码加载到可执行程序(内存利用率不好),生成的可执行程序,就算删除静态库,可执行程序照样能运行(但是:一旦发现了库中有bug,挽救起来就比较麻烦了。必须一一把链接该库的程序找出来,然后重新编译。)
共享库:编译时要找,运行时也要找(运行时,如果这个共享库没有在 /lib或 /usr/lib下,运行不了或者把当前路径加入环境变量LD_LIBRARY_PATH中:export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH),不加载到main。(没静态库快(动态库的不足相对于它带来的好处在现今硬件下简直是微不足道的),但利于多个程序使用,利于后期优化)
创建静态库:ar crv 静态库库名 要添加的.o文件 (c:创建库,r把方法添加到库)
创建共享库: gcc -shared -fPIC –o 共享库库名 要添加的.o文件
gcc -o main main.c -L(指定库在哪)-l(指定库命(不要前后缀))(-l后面必须没有空格)
如果库已被复制到/lib或 /usr/lib,-L就不需要了,但-l一直要写,因为gcc默认只加载c库
ldd 程序名 (查找当前程序运行所需的共享库)
创建的库名:前缀必须是lib 后缀要么so,要么a(静态库)
同一个库如果同时存在动态库和静态库,优先链接动态库,除非使用--static强制使用静态库。
windwos下
静态库和动态库静态库:函数和数据被编译进一个二进制文件(扩展名通常为.lib),在使用静态库的情况下,在编译链接可执行文件时,链接器从静态库中复制这些函数和数据,并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
动态库:在使用动态库时,往往提供两个文件:一个导入库(.lib(导入库lib也被链接到exe文件中去),非必须(动态加载不需要))和一个.dll文件。这里的引入库和静态库文件虽然扩展名都是.lib,但是有着本质上的区别,对于一个动态链接库来说,其引入库文件包含该动态库导出的函数(函数的重定位信息)和变量的符号名,而.dll文件包含该动态库实际的函数和数据。
静态库的pdb文件和调试信息
静态库的pdb文件不能直接加载,那么静态库的调试信息到底是怎么使用的呢?
众所周知静态库的代码直接链接到最终的生成文件里的,那么是不是调试信息也合并到最终的生成文件的pdb文件里呢?
的确如此,链接器在连接的过程中会将静态库的pdb文件中被用到的调试信息解析出来然后合并到最终的生成文件的pdb文件里。
那么链接器是怎么找到静态库的pdb文件的呢?是因为编译器在编译静态库的时候直接将pdb文件的绝对路径写在了obj文件里。链接器通过这个信息就可以找到静态库的pdb文件。
但是这就有个问题,如果把静态库发给别人使用,那么pdb文件路径不就失效了吗?没关系,链接器链接的时候如果找不到绝对路径的pdb文件还会搜索和静态库lib文件放在一起的和原pdb文件同名的pdb文件。
使用动态链接库的好处(动态库只能说在加载时链接(大部分),还是运行时链接(比如我的testlauncher加载dll))可以使用多种编程语言编写:比如我们可以用VC++编写dll,然后在VB编写的程序中调用它。
增强产品功能:可以通过开发新的dll取代产品原有的dll,达到增强产品性能的目的。比如我们看到很多产品踢动了界面插件功能,允许用户动态地更换程序的界面,这就可以通过更换界面dll来实现。
提供二次开发的平台:用户可以单独利用dll调用其中实现的功能,来完成其他应用,实现二次开发。
节省内存:如果多个应用程序使用同一个dll,该dll的页面只需要存入内存一次,所有的应用程序都可以共享它的页面,从而节省内存。
2019/01/04日:从网上找到的博客再次深入
静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
DLL 的编制与具体的编程语言及编译器无关:只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
DLL内的函数分为两种:
(1)DLL导出函数,可供应用程序调用;
(2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
dll入口:
Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、WIN32程序需要WinMain函数一样。在前面的例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。
!
根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。
导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息;相同点:都被链接到exe文件中去。
(重要) 动态链接方法:LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序。
加载静态库和动态库的方式
静态库
在VS中如何添加vc++的.lib文件的方法有2种
方法一:“#pragma comment(lib, "*.lib") ”语句。
示例如下:
#pragma comment(lib,"ws2_32.lib")
上面是添加动态链接库“ws2_32.lib”,把这句话放到文件开头即可,例如:#include
#include
#pragma comment(lib,"ws2_32.lib")
int main()
{...}
方法二:
选择“项目” - “属性” - “配置属性” - “链接器” - “输入” - “附加依赖项”
动态库
静态调用静态库步骤:
1.需要设置lib文件的目录(附加库目录),和链接到该lib文件。例如:通过静态链接方法:#pragma comment(lib, "test.lib"),链接test.lib文件。
动态调用动态库步骤:
1、创建一个函数指针,其指针数据类型要与调用的DLL导出函数相吻合。
2、通过Win32 API函数LoadLibrary()显式的调用DLL,此函数返回DLL的实例句柄。
3、通过Win32 API函数GetProcAddress()获取要调用的DLL的函数地址,把结果赋给自定义函数的指针类型。
4、使用函数指针来调用DLL函数。
5、最后调用完成后,通过Win32 API函数FreeLibrary()释放DLL函数。
DLL和内存管理
加载DLL的每个进程都将其映射到其虚拟地址空间。在进程将DLL加载到其虚拟地址后,它可以调用导出的DLL函数。
系统维护着每个DLL的进程引用计数。当线程加载DLL时,引用计数加1。当进程终止时,或者引用计数变为零时(仅运行时动态链接),DLL将从进程的虚拟地址空间中卸载。