您的位置:首页 > 其它

动态链接库(DLL)和静态库(lib)

2016-12-21 15:55 267 查看
一、 静态链接库与动态链接库区别

静态链接库与动态链接库都是共享代码的方式

lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了,可以删除静态库文件;
DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。
另一个区别在于:

静态链接库中不能再包含其他的动态链接库或者静态库;
而在动态链接库中还可以再包含其他的动态或静态链接库。
动态库就是在需要调用其中的函数时,根据函数映射表找到该函数然后调入堆栈执行。如果在当前工程中有多处对dll文件中同一个函数的调用,那么执行时,这个函数只会留下一份拷贝。但是如果有多处对lib文件中同一个函数的调用,那么执行时,该函数将在当前程序的执行空间里留下多份拷贝,而且是一处调用就产生一份拷贝。

二、静态链接库(lib)

1.在 VS 中新建一个 Win32 项目静态库程序 addDLL





2.添加一个头文件addlib.h 一个源文件addlib.cpp。将要把下面这个函数封装为动态链接库提供给第三方:

//文件:addlib.h
#ifndef ADDLIB_H
#define ADDLIB_H
extern "C" int add(int x,int y);    //声明为C编译、连接方式的外部函数
#endif

//文件:addlib.cpp
#include "addlib.h"
int add(int x,int y)
{
return x + y;
}


编译这个工程就得到了一个 addDLL.lib 文件,这个文件就是一个函数库,它提供了add 函数的功能。将头文件 addlib.h 和 addDLL.lib 文件提供个给用户后,用户就可以直接使用其中的 add 函数了。

3.调用静态库:新建一个工程 libtest,仅包含一个 main.cpp 文件,其源代码如下:

#include <stdio.h>
#include "addlib.h"    //包含头文件
#pragma comment(lib, "addDLL.lib")    //指定与静态库一起连接

int main(int argc, char* argv[])
{
printf( "2 + 3 = %d", add( 2, 3 ));
getchar();
}


注意:需要将
addlib.h 和 addDLL.lib 文件放到当前工程目录下,或者包含文件的绝对路径。

如果不用 #pragma comment 指定,则可在 vs 中添加的 addDLL.lib 文件的路径,如下图:



4.库文件的调试
由于库文件不能单独执行,调试和查看库文件较好的方法是:将库工程和应用工程(调用库的工程)放置在同一工作区,只对应用工程进行调试,在应用工程调用库中函数的语句处设置断点,执行后按下 F11,这样就单步进入了库中的函数。上述调试方法对静态链接库和动态链接库而言是一致的。


三、动态链接库(DLL)
在 vs 中新建一个 DLL 类型的空项目 add



在建立的工程中添加 addDLL.h 及 addDLL.cpp 文件,源代码如下:

/* 文件名:addDLL.h */
#ifndef ADDLIB_H
#define ADDLIB_H
extern "C" int __declspec(dllexport)add(int x, int y);
extern int value;    //导出全局变量
#endif

/* 文件名:addDLL.cpp */
#include "addDLL.h"
int value;    //定义一个全局变量

int add(int x, int y)
{
return x + y;
}


addDLL.h 对函数 add 的声明前面添加了 __declspec(dllexport) 语句,声明函数 add 为 DLL 的导出函数。编译上面的工程,生成文件中包含文件 add.dll 和 add.lib 。

动态库调用:新建一个测试工程,并将文件 add.dll 和 add.lib 放到工程的目录中,同时新建一个 main.cpp 文件:

#include <stdio.h>
#include <windows.h>
typedef int(*pAddFun)(int, int); //宏定义函数指针类型,与函数 add 类型一致

int main(int argc, char *argv[])
{
HINSTANCE hDll; //DLL句柄
pAddFun addFun; //函数指针
hDll = LoadLibrary("add.dll");     //加载指定动态链接库的地址

if (hDll != NULL)
{
addFun = (pAddFun)GetProcAddress(hDll, "add");    //返回指定动态连接库中指定输出函数的地址
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("2 + 3 = %d", result);
}
FreeLibrary(hDll);    //释放链接库
}
return 0;
}


四、DLL 导出变量

DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据。在(三)动态链接库中导出了一个全局变量,一个较为安全的使用 DLL 导出的全局变量方法:

#include <stdio.h>
#pragma comment(lib,"add.lib")    //链接静态库
extern int _declspec(dllimport) value;    //用 _declspec(dllimport) 导入 DLL 中的全局变量

int main(int argc, char *argv[])
{
printf("%d ", value);
value = 1;    //可以直接使用, 无须进行强制指针转换
printf("%d ", value);
return 0;
}


通过 _declspec(dllimport) 方式导入的就是 DLL 中全局变量本身而不再是其地址了,建议在一切可能的情况下都使用这种方式。

五、DLL 导出类

DLL中定义的类可以在调用的工程中使用。

//文件名:point.h,声明 point 类

#ifndef POINT_H
#define POINT_H

#ifdef DLL_FILE
class _declspec(dllexport) point    //导出类 point
#else
class _declspec(dllimport) point    //导入类 point
#endif

{
public:
point() : x(0.0), y(0.0) {}
point(float x_c, float y_c): x(x_c), y(y_c) {}
float add()
{ return x + y; }
private:
float x;
float y;
};

#endif


在类的声明头文件中用一个宏 DLL_FILE 来决定使其编译为 class _declspec(dllexport) point 还是 class _declspec(dllimport) point 版本。为了简单起见,直接在类的声明中对类函数进行了实现。编译上面的工程,生成库文件。

类的引用

将上面工程文件 point.h 和 生成库文件 point.dll 拷贝到当前工程目录下。

#include "point.h"  //包含类声明头文件
#pragma comment(lib,"point.lib");    //链接静态库

int main(int argc, char *argv[])
{
point p(2.0, 2.0);    // 直接使用类 point
printf("p(2.0,2.0).add() = %f \n", p.add());    // 使用类的函数

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息