当前位置: 首页 > 科技观察

深入学习C++静态库和动态库

时间:2023-03-17 22:24:22 科技观察

本次分享的目的是让大家学会创建和使用静态库和动态库,知道静态库和动态库的区别,知道什么时候选择使用它们。这里就不深入介绍静态库和动态库的底层格式和内存布局了。如果你有兴趣,我推荐一本书《程序员的自我修养——链接、装载与库》。什么是图书馆?库是现有的、成熟的、可重用的代码。实际上,每个程序都依赖于许多基础底层库。不可能每个人都从头开始,所以图书馆的存在是很有意义的。本质上,库是可执行代码的二进制形式,可以由操作系统加载到内存中执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。所谓静态和动态是指链接。回顾一下,将程序编译成可执行程序的步骤:静态库之所以成为【静态库】,是因为在链接阶段,汇编生成的目标文件.o会与被引用的库链接起来,打包成可执行文件中。因此,相应的链接方式称为静态链接。试想一下,静态库和程序集生成的目标文件一起链接成一个可执行文件,那么静态库一定是类似于.o文件格式。其实静态库可以简单的看成是目标文件(.o/.obj文件)的集合,也就是很多目标文件压缩打包后形成的文件。静态库特点总结:静态库与函数库的链接是在编译时完成的。程序运行时与函数库无关,移植方便。浪费空间和资源,因为所有相关的目标文件和涉及的库都链接到一个可执行文件中。下面写一些简单的四算术C++类,编译成静态库供别人使用。头文件如下:#pragmaonceclassStaticMath{public:StaticMath(void);~StaticMath(void);staticdoubleadd(doublea,doubleb);//加法staticdoublesub(doublea,doubleb);//减法staticdoublemul(doublea,doubleb);//乘法staticdoublediv(doublea,doubleb);//除法voidprint();};linux下使用ar工具,windows下使用libvs.exe,目标文件压缩在一起,编号索引方便搜索和检索。创建静态库的一般步骤如图:#p#Linux下创建和使用静态库Linux静态库命名规则Linux静态库命名约定必须是“lib[your_library_name].a”:lib是前缀,中间是static库的名字,扩展名为.a。创建静态库(.a)通过上面的过程可以知道Linux中创建静态库的过程如下:首先将代码文件编译成目标文件.o(StaticMath.o)g++-cStaticMath.cpp注意参数-c,否则直接编译成可执行文件然后通过ar工具ar-crvlibstaticmath.aStaticMath.o将目标文件打包成.a静态库文件生成静态库libstaticmath。A。比较大的项目会写makefile文件(CMake等项目管理工具)生成静态库,输入多条命令太麻烦。使用静态库编写使用上面创建的静态库的测试代码:#include"StaticMath.h"#includeusingnamespacestd;intmain(intargc,char*argv[]){doublea=10;doubleb=2;cout<<"a+b="<usingnamespacestd;intmain(intargc,char*argv[]){doublea=10;doubleb=2;cout<<"a+b="<usingnamespacestd;int_tmain(intargc,_TCHAR*argv[]){doublea=10;doubleb=2;cout<<"a+b="<提供如下接口:void*dlopen(constchar*pathname,intmode):该函数以指定模式打开指定动态链接库文件,并返回一个句柄到调用过程。void*dlsym(void*handle,constchar*symbol):dlsym根据动态链接库操作句柄(pHandle)和符号(symbol)返回符号对应的地址。使用这个函数不仅可以得到函数的地址,还可以得到变量的地址。intdlclose(void*handle):dlclose用于关闭指定句柄的动态链接库,只有当该动态链接库的使用次数为0时,系统才会真正卸载。constchar*dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回错误信息。当返回值为NULL时,表示操作函数执行成功。Windows下显式调用动态库应用程序必须在运行时进行函数调用以显式加载DLL。要显式链接到DLL,应用程序必须:调用LoadLibrary(或类似函数)以加载DLL并获取模块句柄。调用GetProcAddress以获得指向应用程序要调用的每个导出函数的函数指针。由于应用程序通过指针调用DLL的函数,编译器不会产生外部引用,所以不需要与导入库链接。使用DLL后调用FreeLibrary。显式调用C++动态库注对于C++,情况稍微复杂一些。显式加载C++动态库的部分困难在于C++的名称修改;另一部分是它没有提供适当的API来加载类。在C++中,你可能想使用库中的一个类,这需要创建那个类的一个实例,这并不容易做到。名称修改可以通过extern"C"来解决。C++有一个特定的关键字用于声明带有C绑定的函数:extern"C"。用extern"C"声明的函数将使用函数名作为符号名,就像C函数一样。因此,只能将非成员函数声明为extern"C",不能重载。尽管有这些限制,extern"C"函数还是非常有用,因为它们可以像C函数一样由dlopen动态加载。加上extern“C”限定符后,并不意味着函数中不能使用C++代码。相反,它仍然是一个完整的C++函数,可以使用任何C++特性和各种类型的参数。另外,如何从C++动态库中获取类,附上几篇相关文章,不过我不推荐这个:《LoadLibrary调用DLL中的Class》:http://www.cppblog.com/codejie/archive/2009/09/24/97141.html?:http://blog.csdn.net/denny_233/article/details/7255673“显式”使用C++动态库中的Class非常麻烦和危险,所以可以“隐式”使用Don不要使用“显式”,如果可以是静态的,就不要使用动态的。附:Linuxg++(gcc)编译选项下库相关命令-shared:指定生成动态链接库。-static:指定生成静态链接库。-fPIC:表示编译为与位置无关的代码,用于编译共享库。目标文件需要创建为与位置无关的代码,这在概念上意味着它们可以在可执行文件加载它们时放置在可执行文件内存中的任何位置。-L。:表示要链接的库所在的目录。-l:指定链接所需的动态库。编译器在查找动态链接库时有一个隐含的命名规则,即在给定名称前加lib,在其后加.a/.so来确定库名。-Wall:生成所有警告消息。-ggdb:此选项将生成尽可能多的调试信息供gdb使用。-g:编译器在编译时产生调试信息。-c:只激活预处理、编译和汇编,即把程序做成目标文件(.o文件)。-Wl,options:将参数(options)传递给链接器ld。如果options中间有一个逗号,options会被分成多个options,然后传递给linker。nm命令有时可能需要检查库中有哪些函数。nm命令可以打印出库中涉及的所有符号。库可以是静态的也可以是动态的。nm列出的符号很多,常见的有3种:一种是在库中调用,但没有在库中定义(说明需要其他库支持),用U表示;一种是库中定义的函数,用T表示,最常用;一种是所谓的“弱状态”符号,虽然在库中定义,但可能会被其他库中的同名符号覆盖,用W表示。$nmlibhello.hlddcommandldd命令可以查看共享库一个可执行程序依赖于。比如我们写的四个算术运算的动态库依赖于以下库:综上所述,两者的区别在于代码加载的时刻。静态库将在程序编译时链接到。在目标代码中,程序运行时将不再需要静态库,因此体积较大。动态库在程序编译时不会与目标代码连接,而是在程序运行时加载。动态库在运行时也需要存在,所以代码量小。动态库的好处是如果不同的应用程序调用同一个库,那么内存中只需要共享库的一个实例。带来好处的同时,也会有问题!比如经典的DLLHell问题,如何避免动态库管理问题可以找相关资料。