静态库lib和动态dll的区别及使用方法

windows中静态库lib和动态dll的区别及使用方法

1. 静态库lib和动态dll的区别

~1. 静态库

  编译链接的流程:

    1) 预处理

    2) 编译, cpp -> obj文件

    3) 链接, obj -> exe文件

 

  静态库, 多个obj文件的集合, 多个obj文件合成一个lib

  使用静态库:

    1) 拷贝头文件,包含头文件

    2) lib添加到工程

       2.1 项目属性->连接器->输入->附加依赖项

       2.2 #pragma comment(lib, "..\\debug\\staticlib.lib")

       2.3 拖到工程中

 

   为了使一个头文件同时在cppc文件中使用,可以使用下面的宏:

   #ifdef __cplusplus

    extern "C" {

   #endif // __cplusplus

 

int  MyAdd(int nVal1, int nVal2);

extern int g_nValTest;

 

   #ifdef __cplusplus

   }

   #endif // __cplusplus

  静态库的缺点:

    1.1. 每次使用都要重新编译

   1.2. 多个程序使用同一个静态库,每个程序中都会有一份静态库代码的拷贝,会造成磁盘的资源的浪费

 

~2. 动态库

  静态库在编译阶段将代码加入到exe,运行时不再需要静态库

  动态库在编译阶段代码并不加入到exe,是在运行时将代码加入到进程

 

  默认情况下,动态库中的函数禁止外部项目使用,如果使用,需要动态库指明此函数可以被外部项目使用, 可以被外部使用的函数, 导出函数.

  外部项目使用动态库中函数,需要指明此函数为某库的导出函数.

  

  导出函数:

   1. __declspec(dllexport) int MySub(int nVal1, int nVal2);

  显示使用:

   1. 包含头文件

   2. dllexport改为dllimport

   3. 添加库

  为了在拷贝动态库头文件时不用修改dllexportdllimport,可以使用下面的宏:

#ifdef DLL_EXPORTS

#define DLL_API __declspec(dllexport)

#else

#define DLL_API __declspec(dllimport)

#endif // DLL_EXPORT

 

 如何在dll的项目中调试程序:

将加载dllexe的路径填入 项目属性选项->调试->命令

 

1.1 项目类型

VS在建Win32项目时,有以下选项:

windows应用程序
控制台应用程序
DLL
静态库
最后两个类型:DLL和静态库,这两种项目类型是不可以单独运行的,必须在Windows应用程序调用他们执行,是提供的库函数而已。

1.2 两种lib的区别:

1)静态库(.lib

函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。

2)动态库(.lib文件和.dll文件)

在使用动态库的时候,编译后往往提供两个文件:一个引入库(.lib)文件(也称导入库文件)和一个DLL.dll)文件。当然到了后面会告诉你如果只提供一个DLL文件,使用显示连接的方式也可以调用,只是稍加麻烦而已。

虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别。对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不可复制到可执行文件,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。这时,在发布产品时,除了发布可执行文件以外,同时还需要发布该程序将要调用的动态链接库。

只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。如果DLL不在内存中,系统就将其加载到内存中。当链接Windows程序以产生一个可执行文件时,你必须链接由编程环境提供的专门的 引入库(import library)”。这些引入库包含了动态链接库名称和所有Windows函数调用的引用信息。链接程序使用该信息在.EXE文件中构造一个表,当加载程序时,Windows使用它将调用转换为Windows函数。

引入库LIb和静态库Lib的区别:

引入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于引入库而言,其实际的执行代码位于动态库中,引入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。但是引入库文件的引入方式和静态库一样,要在链接路径上添加找到这些.lib的路径。

1.3 动态库(LibDLL)和静态库Lib的区别: 
其实上面已经提到了区别,这里再总结一下: 
1)静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了,最终的可执行文件exe会比较大。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以动态地引用和卸载这个与 EXE 独立的 DLL 文件。 
2)静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接 库。静态链接库与静态链接库调用规则总体比较如下。

静态链接库运行之前就加载了,而且一直存在直到关闭程序,动态DLL实在运行时再加载,不用一直占内存,dll模块内部更改了,是要替换Dll即可,方便维护。优点明显,但是dll如果丢失或被误删,就无法运行了

2. 静态库lib和动态dll的使用

2. 1 动态dll的使用

动态链接库的使用需要库的开发者提供生成的.lib文件和.dll文件。或者只提供dll文件。使用时只能使用dll中导出的函数,未导出的函数只能在dll内部使用。Dll的调用有显示连接和隐式连接两种:隐式连接需要三个东西,分别是*.h头文件,lib库(动态的),DLL库;显示连接只需要.dll文件即可。

2.1.1 隐式连接 
隐式链接需要三个东西,分别是*.h头文件,lib库(动态的),DLL库,而这里的lib库仅是编译的时候用,运行时候不用,运行时只用Dll

2.1.1.1 添加Lib

方法1: 通过设置工程配置来添加lib.

A、添加工程的头文件目录:工程->属性->配置属性->c/c++->常规->附加包含目录:加上头文件存放目录。添加头文件参考2.2.1.2 
B、添加文件引用的lib静态库路径:工程->属性->配置属性->链接器->常规->附加库目录:加上lib文件存放目录。 
C 然后添加工程引用的lib文件名:工程->属性->配置属性->链接器->输入->附加依赖项:加上lib文件名。

这种方法比较繁琐,且不直观,而且还可能要争对debug版本和release版本作不同的配置,因为我们生成的两个版本的库可能放在不同的目录中的.

方法2: 使用编译语句:

#ifdef _DEBUG
#pragma comment(lib,"..\\debug\\LedCtrlBoard.lib")
#else
#pragma comment(lib,"..\\release\\LedCtrlBoard.lib")
#endif
1
2
3
4
5
这种方法直观,方便,且可以按如上直接区分出Debug版本和Release版本的不同目录.当然,通过宏,还可以区分更多版本.但是在指定目录时,不小心容易出错.

方法3: 直接添加库文件到工程中.

就像你添加.h.cpp文件一样,lib文件添加到工程文件列表中去.

VC,切换到解决方案视图”—>选中要添加lib的工程–>点击右键–>”添加”–>”现有项”–>选择lib文件–>确定.

这个方法适用于在我的工程的debug版本和Release版本中都使用同一个lib库文件时.这样就省去了你1方法配置环境的繁琐,也省去了方法2种语句的可能性错误发生.

2.1.1.2添加头文件

在调用DLL中导出的函数之前要include对应的头文件,可以写绝对路径,也可以拷贝到工程调用源文件的同一目录下,也可以通过VS添加(include)头文件目录,VS中配置方法: 
1VS项目->右击属性->配置属性->VC++目录->包含目录 
2VS项目->右击属性->配置属性->C/C++->常规->附加包含目录

2.1.1.3 添加dll

一般将dll拷贝到运行时目录即可,与调用者exe文件在同一目录,当然有其他方法添加环境变量PATH 
1VS项目->右击属性->配置属性->VC++目录->可执行目录 
2)设定DLL目录的位置,具体方法为:项目右击->属性 -> 配置属性 -> 调试 ->工作目录,在这里设置dll的路径就可以了

1release版本和debug版本的区分,每种版本的设置都是独立的,要分别设置 
2:单纯添加lib目录的方法有以下几种方法

1):把.lib文件放在当前使用它的工程目录下;(如:.exe所在目录,或者工程代码所在的目录) 
2):对某一个项目:项目”->“属性”->“配置属性”->“VC++目录”->“常规”->“附加库目录” 
3):在vs中,项目”->“属性”->“配置属性”->“链接器”->“常规”->“附加库目录” 
4):放在开发环境IDElib库目录下,例如:“C:\Program Files\Microsoft Visual Studio 8\VC\lib”,这是vs2005vc开发的lib库目录。

注:在VS属性中配置路径时可以用绝对路径,也可以用相对路径,其中./表示当前目录,而../表示上一级目录

上面仅仅对单个项目有效,我们配置过opencv库的都知道,有一种是全局配置libinclude头文件,对所有的项目有效,以Debug版为例,步骤如下
(1) 点击视图”→“其他窗口”→“属性管理器” 
(2) 从左侧项目中打开“Debug| Win32”→“Microsoft.Cpp.Win32.user” 
(3) 双击“Microsoft.Cpp.Win32.user”,在弹出的窗口,点击左侧VC++目录,编辑右侧的可执行文件目录、包含目录与库目录,分别添加对应的路径 
(4) 附加依赖项,单击链接器”→“输入”→“附加依赖项,填入依赖项.lib后缀的文件名。

2.1.2 显示连接

隐式链接虽然实现较简单,但除了必须的.dll文件外还需要DLL.h文件和.lib文件,在那些只提供.dll文件的场合就无法使用,而只能采用显式链接的方式。这种方式通过调用API函数来完成对DLL的加载与卸载,能更加有效地使用内存,在编写大型应用程序时往往采用此方式。这种方法编程具体实现步骤如下:

使用Windows API函数Load Library或者MFC提供的AfxLoadLibraryDLL模块映像到进程的内存空间,对DLL模块进行动态加载。

使用GetProcAddress函数得到要调用DLL中的函数的指针。

不用DLL时,用Free Library函数或者AfxFreeLibrary函数从进程的地址空间显式卸载DLL

使用LoadLibrary显式链接,那么在函数的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位搜索DLL

包含EXE文件的目录
工程目录
Windows系统目录
Windows目录
列在Path环境变量中的一系列目录
2.2 静态库lib的使用

静态lib中,一个lib文件实际上是任意个obj文件的集合,obj文件是cpp文件编译生成的。静态库的.lib文件包含了链接库的所有信息(函数代码和接口信息)。所以我们在调用静态库.lib时,只需要包含头文件目录(../include. .h),以及附加库目录即可。因此,静态链接库的使用需要库的开发者提供生成库的.h头文件和.lib文件

VC中新建一个static library类型的工程TestLib,加入test.cpp文件和test.h文件(头文件内包括函数声明),然后编译,就生成了TestLib.lib文件。

别的工程要使用这个lib方式:

1)添加lib 
方法1):直接用项目右击”->”添加”–>”现有项”–>选择lib文件–>确定,通过这种方式将.lib加入工程 
方法2):工程属性-> 配置属性->链接器->输入->附加依赖项中添加要使用的Lib库的名字;在工程属性-> 配置属性->链接器->输入->附加库目录中输入.lib文件所在路径(相对或绝对路径)

方法3):或者在源代码中加入指令#pragma comment(lib, “TestLib.lib”),也可以指定完整路径(绝对路径或相对路径)#pragma comment(lib, “..\Debug\TestLib.lib”)。可以通过宏#if defined(_DEBUG)来区分用releasedebug版本的lib。另外这里如果不指定完整路径,也要像方法2一样添加附加库目录。

如果不在工程属性中添加附加lib库目录,也可以将静态里边库比如TestLib.lib拷贝到工程所在目录,或者拷贝到执行文件生成的目录,或者拷贝到系统Lib目录中。

2. 添加头文件

加入相应的头文件test.h#include “test.h”

include file path可以为绝对路径,也可以为相对于工程所在目录的相对路径,如果头文件比较多,可以在project>settings>c/c++>preprocessorAdditional include directories中填入你的头文件所在目录,编译时会自动在此目录查找头文件。

················································

c++.dll.lib文件的生成与使用的详解

两种库:

  包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library
  包含函数代码本身,在编译时直接将代码加入程序当中,称为静态链接库static link library
共有两种链接方式:

  动态链接使用动态链接库,允许可执行模块(.dll文件或.exe文件)仅包含在运行时定位DLL函数的可执行代码所需的信息。
  静态链接使用静态链接库,链接器从静态链接库LIB获取所有被引用函数,并将库同代码一起放到可执行文件中。

两种文件的区别

使用lib需注意两个文件:

•.h头文件,包含lib中说明输出的类或符号原型或数据结构。应用程序调用lib时,需要将该文件包含入应用程序的源文件中。
•.LIB文件。

使用dll需注意三个文件:

•.h头文件,包含dll中说明输出的类或符号原型或数据结构的.h文件。应用程序调用dll时,需要将该文件包含入应用程序的源文件中。
•.LIB文件,是dll在编译、链接成功之后生成的文件,作用是当其他应用程序调用dll时,需要将该文件引入应用程序,否则产生错误(如果不想用lib文件或者没有lib文件,可以用WIN32 API函数LoadLibraryGetProcAddress装载)。
•dll文件,真正的可执行文件,开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,并不需要.lib文件和.h头文件。

1.生成lib文件

首先,我们先建立一个控制台工程(新建->工程->控制台程序),添加sub.cpp以及sub.h文件

//sub.h#ifndef _SUB_H

#define _SUB_H

void sub(int a,int b);

#endif//sub.cpp

#include "sub.h"

#include <iostream>void sub(int a,int b)

{

    std::cout<<(a-b)<<std::endl;

}

由于在工程中,没有main()函数,所以直接编译出错。这时,要右键点击工程,并选择工程属性,选择静态链接库即可。这时候再按F7build solution即可产生lib文件。在Debug中只生成.lib文件。

2.生成dll文件

  生成dll文件的过程与上面的过程是一样的,只是在选择Dynamic Library(.dll)即可。在Debug中会生成一个.lib.dll两种文件。

 

3.两种文件的使用

在使用时,静态链接库只要把.h.lib文件加入到工程文件夹中即可。而动态链接库要把.h.lib.dll文件加入到工程中。

#include <iostream>

#include "sub.h"  //链接库的头文件using namespaces std;#pragma comment(lib,"sub.lib") //加入链接库int main()

{

    sub(5,4);

    return 0;

}

.h.lib的文件放在相应的目录下 ,有时候有错误,但是也能运行成功。

感觉靠谱的方法是像一些标准库的方法,添加包含目录和库目录,添加依赖项。

第一种方法:

把这个库拷贝到工程目录下,(最好拷贝到vs编译器可以找到的目录下,这个路径在tools---options---projects and soulutions---show libraray的目录下,至于这些目录如何查看,在以前的博客中有相关的文章,这样可以方便编译器编译时候查找到这个库文件)之后把库头文件拷贝到当前目录下

libtest的实现文件中,首先包含这个头文件,然后使用pragma指令指明链接库chang.lib,然后再工程中使用这个函数中的库就可以了

第二种方法:

这个库文件可以在任意路径下,首先在tools---options---projects and soulutions---show libraray目录下添加这个库文件所在的目录

然后再在工程属性---configuration properties---Linker---Addtional library directories下添加这个库文件所在的目录

 

 

在工程属性---configuration properties---Linker---Input---Addtional dependencies添加要使用的库的名字,现在这个就是chang.lib

之后把库文件的头文件拷贝到libtest工程的源文件文件夹下

最后在源文件中就可以不使用pragma指令了

//另注:生成 .lib文件的工程中可以没有main()函数。

//另注:工程test1之所以生成的是 .lib文件而不是 .dll文件,是因为test1test2在同一个解决方案中,没有涉及外部接口。

 

 

posted @ 2020-09-04 12:50  特权E5  阅读(432)  评论(0编辑  收藏  举报