美文网首页
静态库和动态库

静态库和动态库

作者: 朔方烟尘 | 来源:发表于2019-02-25 22:46 被阅读0次
    计算机编译系统.JPG

    1. 静态库

    在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

    试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(****.o/.obj****文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:

    • 静态库对函数库的链接是放在编译时期完成的。

    • 程序在运行时与函数库再无瓜葛,移植方便。

    • 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

    2.动态库

    为什么需要动态库?

    • 空间浪费是静态库的一个问题。

    • 另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

    动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新

    动态库特点总结:

    • 动态库把对一些库函数的链接载入推迟到程序运行的时期。

    • 可以实现进程之间的资源共享。(因此动态库也称为共享库)

    • 将一些程序升级变得简单。

    • 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

    Window与Linux执行文件格式不同,在创建动态库的时候有一些差异。

    • 在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)****关键字

    • Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

    与创建静态库不同的是,不需要打包工具(ar、lib.exe),直接使用编译器即可创建动态库。

    StaticLibrary.lib的大小为190KB,DynamicLibrary.lib的大小为3KB,静态库对应的lib文件叫静态库,动态库对应的lib文件叫【导入库】。实际上静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息

    3.动态库的隐式调用

    设置输出目录

    配置属性-->常规
    输出目录:自定义相对路径 ..\Lib,相对路径的起点为xxx.vcxproj 文件所在的目录
    中间目录:
    win32:$(Configuration)
    x64:$(PlatformName)$(Configuration)
    目标文件名:根据debug\release 与 win32\x64 组合,设置不同的后缀以区分
    $(PlatformName)_Debug
    $(PlatformName)_Debug_x64
    $(PlatformName)_Release
    $(PlatformName)_Release_x64

    新建测试程序

    设置引用
    配置属性-->C/C++:常规-->附加包含目录:实际的头文件目录..\Include
    配置属性-->链接器:常规-->附加库目录:实际的库文件目录..\Lib
    配置属性-->链接器:输入-->附加依赖项:实际的库文件xxx.lib

    运行测试程序,需要把xxx.dll拷贝到exe文件所在目录

    4.动态库的显式调用

    上面介绍的动态库使用方法和静态库类似属于隐式调用,编译的时候指定相应的库和查找路径。其实,动态库还可以显式调用。【在****C****语言中】,显示调用一个动态库轻而易举!

    4.1. 在Linux下显式调用动态库

    #include <dlfcn.h>,提供了下面几个接口:

    • void * dlopen( const char * pathname, int mode ):函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。

    • void* dlsym(void* handle,const char* symbol):dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。

    • int dlclose (void *handle):dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

    • const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    4.2. 在Windows下显式调用动态库

    应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须:

    • 调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。

    • 调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。

    • 使用完 DLL 后调用 FreeLibrary

    4.3.显式调用C++动态库注意点

    对C++来说,情况稍微复杂。显式加载一个C++动态库的困难一部分是因为****C++的name mangling另一部分是因为没有提供一个合适的****API****来装载类,在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。

    name mangling可以通过extern "C"解决。C++有个特定的关键字用来声明采用C binding的函数: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

    -《C++ dlopen mini HOWTO》:
    http://blog.csdn.net/denny_233/article/details/7255673

    “显式”使用C++动态库中的Class是非常繁琐和危险的事情,因此能用“隐式”就不要用“显式”,能静态就不要用动态。

    总结

    二者的不同点在于代码被载入的时刻不同

    • 静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大

    • 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小

    动态库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。带来好处的同时,也会有问题!如经典的DLL Hell问题,关于如何规避动态库管理问题,可以自行查找相关资料。

    相关文章

      网友评论

          本文标题:静态库和动态库

          本文链接:https://www.haomeiwen.com/subject/xjggyqtx.html