使用DLL的时机和好处分别是什么呢?

2021-05-07 17:43发布

6条回答
studentaaa
2021-05-08 15:14

静态链接库DLL,动态连接库概念以及使用dll的好处:


静态连接库

静态连接库:Window下以*.lib Linux下以*.a 命名的文件统称;

静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序发布运行的时候不再需要其它的库文件


动态连接库

动态连接库(Dynamic-link Library,缩写为 DLL):Window下以*.dll Linux下以*.so 命名的文件统称;

动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此在发布和运行时需要相应DLL文件的支持


分析

分隔符上边是优点下边是缺点


静态链接库 动态链接库

代码装载速度快,执行速度略比动态链接库快 更加节省内存并减少页面交换

只需保证在开发者的计算机中有正确的.LIB文件,在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题,可避免DLL地狱等问题 DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性

不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数

适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试

使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费;是将全部指令包含到可执行程序,要么全要要么全不要 应用程序不是自完备的,依赖的DLL模块必须存在。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要修改旧模块才能运,会造成"DLL地狱"

无论动态库还是静态库都包含 .lib文件

我接触的大多是使用动态链接库,静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.a更新了,所以使用它的应

用程序都需要重新编译、发布给用户(对于客户来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。   

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存

里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和

发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

静态链接库调用

静态链接库的使用比较简单需要库的开发者提供生成库的.h头文件和.lib文件。生成库的.h头文件中的声明格式如下:


extern "C" 函数返回类型 函数名(参数表);

在调用程序的.cpp源代码文件中如下:


#include "../lib.h"

#pragma comment(lib,"..//debug//libTest.lib")      //指定与静态库一起链接

需要的文件: 头文件 .h 、静态库 .lib

头文件.h中有函数的声明,使用静态链接库的项目需要引用该文件才能编译通过

lib包含了实际执行代码、符号表等等

加载lib的方法: 法1.使用编译链接参数或者VS的配置属性来设置 法2.使用pragma编译语句,例如pragma comment(lib,“a.lib”)

lib中的指令将全部被直接包含在最终生成的 EXE 文件中

动态链接库俩种调用方式

动态链接库的调用方式有俩种比较拗口:静态调用(隐式加载) 和 动态调用(显示加载)


先来说比较常用和简单的隐式加载

需要生成的 lib dll .h 文件 三者缺一不可,配置如下:

vs项目->属性->VC++目录->包含目录:添加头文件所在路径

vs项目->属性->VC++目录->库目录:添加lib文件所在路径

DLL复制到exe执行目录

此处我有个疑问有懂得大佬可以帮我解答下:为什么有的还需要在链接器->附加依赖项中添加 lib文件的全称

什么情况下添加?添加的作用? 是和导出方式有关么


和静态库有什么区别:其实这里生成的 lib 文件是很小的。 它只保存了 dll 文件的符号信息,保证 exe 链接的时候能找到要链接的符号,而运行的时候才是真正加载 dll 来调用函数。静态库的话是会把所有实现都会生成 lib 文件,所以静态的 lib 文件是很大的。


 优点:

1.不需要动态加载 dll 文件和释放。在写代码的时候引入了头文件,对于 dll 导出的函数和数据结构看起来比较清晰。


 缺点:

1.因为隐式加载的 dll 是在 main 函数之前,所以如果缺少了 dll 会影响 exe 的启动。


显式加载


这里大概可以分这几步:1.加载dll文件,2。取得文件内相应函数的指针。3.使用函数。4.释放dll句柄


只需要生成的 dll 文件 ,但是loadLibrary之后可以使用getProcAddress来查找一个函数的地址从而调用该函数,必须知道函数的类型,名称,参数 

DLL复制到exe执行目录


typedef   int(__cdecl*   FunctionAdd)(int,int); // 声明相应的函数指针类型。 

HMODULE   hModule; // 指向dll文件的句柄。

FunctionAdd   add; // 指向相应函数的指针实例。

hModule   =   LoadLibrary("dll1.dll"); // LoadLibrary 将dll1.dll文件加载进内存中来。用句柄指向。文件目录可以任意。

If(NULL==hModule   )

{

 //error.

}

add   =(FunctionAdd)GetProcAddress(hModule,"add"); //根据dll的句柄得到相应函数的指针。

if(NULL==add)

{

 //error

}

int r = add(1,1) ;  // 得到指针后就可以用相应的函数啦。

Wsprintf msgbox

 FreeLibrary(hModule); // 释放dll句柄。

 优点:

1.不需要在 exe 链接的时候放入对应的 lib。

2.加载的 dll 在 main函数之后,是由用户主动操作,可操作空间大。

3.就算目标 dll 丢失,也只会导致 LoadLibrary加载失败,根据业务逻辑来判断是否影响主逻辑。

4.支持热更新,更新了 dll,在进程内部调用 FreeLibrary 然后调用LoadLibrary 加载新的 dll,而不用重启进程。

 缺点:

1.调用之前需要清楚的知道导出函数的定义,来定义函数指针指向它。需要手动加载和释放,如果漏掉了释放可能会造成资源泄露。

根据需求来选择加载模式,比如这个 dll 是程序的主模块,没有的话程序就不能启动,使用隐式加载。而比如dll可能就只是一个插件,这样就可以使用显示加载


声明一个导出函数,是说这个函数要从本DLL导出。我要给别人用。一般用于dll中省掉在DEF文件中手工定义导出哪些函数

的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类


声明一个导入函数,是说这个函数是从别的DLL导入。我要用。一般用于使用某个dll的exe中 不使用 __declspec(dllimport) 

也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因

为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 

边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

一周热门 更多>