动态库和静态库的区别(动态库和静态库的区别和联系)

动态库和静态库的区别(动态库和静态库的区别和联系)一 静态库与动态库库 library 一般是一种可执行的二进制格式 **作系统载入内存执行 静态库 在链接的时候 链接器将目标文件 obj 和用到的静态库一起打包到最后生成的可执行文件中 因此 使用了静态库的可执行程序存储在磁盘上的空间就比较大 Windows 上的静态库是 lib 文件 但和 dll 文件的 lib 文件是不同的 下面会有阐述 动态库

大家好,我是讯享网,很高兴认识大家。



静态库:

在链接的时候,链接器将目标文件(.obj)和用到的静态库一起打包到最后生成的可执行文件中。因此,使用了静态库的可执行程序存储在磁盘上的空间就比较大。Windows上的静态库是.lib文件(但和dll文件的.lib文件是不同的,下面会有阐述)。

动态库:

在可执行程序被加载到内存中执行的时候,才会去加载动态库。Widows上的动态库是dll文件(Dynamic Linked Library)。

静态库与动态库的区别:

1. 使用了静态库的程序存储在磁盘上的空间比使用了动态库的程序要多。

2. 使用了动态库的程序,若有多个副本在内存中执行,又或者是不同的程序但都使用了同一个动态库,则它们所调用的动态库在内存中只有一份,所以很节省内存空间;而使用了静态库的程序的多个副本在内存中时,它们所使用的库所占的内存也是多份,因此浪费空间。

3. 在库需要升级的时候,使用动态库的程序只需要升级动态库就好(假设接口不变),而使用了静态库的程序则需要升级整个程序。

 

在Windows操作系统中,Visual Studio使用lib.exe作为库的管理工具,负责创建静态库和动态库。

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

 

在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数作为初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。
Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

2. 然后,使用库管理器 Lib.exe 链接代码 (lib StaticMath.obj),创建静态库StaticMath.lib。

 

使用静态库有2个方法:

在 Project’s Properties -> Linker -> Input ->Additional Dependencies,添加静态库的文件名。

注意,静态库的文件名叫xxx.lib,和动态库的导入库文件的文件后缀名相同。二者内容当然是完全不同的:静态库文件当然包含了执行代码和符号表,而动态库的导入库文件则只包含了地址符号表。

 

先来看看它们的作用:

1. __declspec(dllexport)可以被用来修饰一个函数或一个类,以表明这是一个导出函数或导出类。所以,这个宏是用在为dll做编程实现的时候的。

当修饰类的时候,该宏要写在class关键字的后面、类名的前面;

当修饰函数的时候,要写在函数声明的前面,而因为name mangling的问题,在此宏的前面还要写上extern“C”. 比如:

 

extern “C” MYDLL_DECL_EXPORT void say_hello();
2. declspec(dllimport) 被用来在调用dll的程序里,表明该程序要调用的某个函数是import自某动态库的。所以,该宏的具体位置是在对dll进行描述的头文件中的。

 

3. 从以上可以看出,在dll的实现中,我们需要declspec(dllexport)来表明这些函数和类是导出函数和导出类,而在使用dll的程序中,又要用declspec(dllimport)来表明它所描述的函数或类是来自于某dll。那么这样的话,岂不是需要2个不同但又很相近的头文件来做这些函数和类的声明了吗?能否将这2个函数合并成一个呢?答案是可以的 – 使用宏进行判断:当宏A存在时,就认为宏B是declspec(dllexport),否则就认为宏B是__declspec(dllimport)。具体实例如下:

 

#ifdef MYDLL_EXPORTS
#define MYDLL_DECL_EXPORT __declspec(dllexport)
#else
#define MYDLL_DECL_EXPORT __declspec(dllimport)
#endif

所以,在dll的实现中,需要在Preprocessor Definitions里定义MYDLL_EXPORTS,而在dll的使用者那里就不需要定义MYDLL_EXPORTS了。

 


讯享网

在 Project’s Properties -> Linker-> General -> Additional Library Directories,添加动态库的导入库文件(即.lib文件)的所在目录;

在Project’s Properties -> Linker -> Input ->Additional Dependencies,添加动态库的导入库的文件名。

隐式调用的优点是,既可以使用导出函数,也可以使用导出类。

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

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

c. 使用完 DLL 后调用 FreeLibrary。

显式调用有一个比较大的问题,就是很难使用导出类。很难并不是不可以,可以用专业工具修改.def并将类也声明为extern“C”的方式来做到,但是是不推荐这样做的。具体文献可以参考这里:

http://www.cppblog.com/codejie/archive/2009/09/24/97141.html

http://blog.csdn.net/denny_233/article/details/

 

其实,搜索动态库文件dll的顺序是这样的:

1. 包含EXE文件的目录,

2. 进程的当前工作目录,

3. Windows系统目录,

4. Windows目录,

5. 列在Path环境变量中的一系列目录。

 

首先,我们来创建一个动态库,叫做MyDll.dll. 这首选需要在VS下创建一个dll的工程,具体过程就不赘述了。总共只需要2个文件,MyDll.h 和 MyDll.cpp,详细代码如下:

MyDll.h如下:

 

#ifndef MYDLL_H
#define MYDLL_H

#ifdef MYDLL_EXPORTS
# define MYDLL_DECL_EXPORT __declspec(dllexport)
#else
# define MYDLL_DECL_EXPORT __declspec(dllimport)
#endif

class MYDLL_DECL_EXPORT MyDll {
public:
MyDll(int x=0, int y=0);
MyDll(const MyDll &) = delete;
~MyDll(){}
MyDll operator = (const MyDll &) = delete;

long Add();
long Sub();

int GetFirstNumber();
void SetFirstNumber(int x);
int GetSecondNumber();
void SetSecondNumber(int y);

private:
int mFirstNum;
int mSecondNum;
};

extern “C” MYDLL_DECL_EXPORT void say_hello();

#endif

MyDll.cpp如下:

 

 

#include “MyDll.h”
#include <iostream>

MyDll::MyDll(int x, int y) :mFirstNum(x), mSecondNum(y){}

long MyDll::Add() {
return long(mFirstNum) + long(mSecondNum);
}

long MyDll::Sub() {
return long(mFirstNum - mSecondNum);
}

int MyDll::GetFirstNumber() {
return mFirstNum;
}

int MyDll::GetSecondNumber() {
return mSecondNum;
}

void MyDll::SetFirstNumber(int x) {
mFirstNum = x;
}

void MyDll::SetSecondNumber(int y) {
mSecondNum = y;
}

// standalone function but also exposed
MYDLL_DECL_EXPORT void say_hello() {
std::cout << “Hello, World!” << std::endl;
}

这个dll工程build后,就可以生成MyDll.dll和MyDll.lib了。但是要注意,在Preprocessor Definitions中要记得添加上MYDLL_EXPORTS宏。否则就没啥导出的了。

 

然后,再建一个Windows Console工程,里面只需要一个cpp文件:

 

#include <Windows.h>
#include “https://www.cnblogs.com/ToTigerMountain/MyDll/MyDll.h”
#include <iostream>
using namespace std;

void way_1_with_lib_and_dll() {
cout << “In way 1: with lib and dll” << endl;
MyDll m1(10, 20);
long add_result = m1.Add();
cout << “add result is ” << add_result << endl;
cout << “Way 1 ends” << endl;
}

void way_2_with_loadlibrary() {
cout << “In way 2: with loadlibrary” << endl;

HMODULE handle = LoadLibrary(L”C:\Users\Finix\Documents\Visual Studio 2013\Projects\TestDLL\Debug\TestDLL.dll”);
if (handle) {
cout << “handle Not NULL!” << endl;

typedef void(*SAYHELLO)();
SAYHELLO func = reinterpret_cast<SAYHELLO>(GetProcAddress(handle, “say_hello”));
func();

if (!FreeLibrary(handle)) {
cout << “Free failed!” << endl;
}
}

cout << “Way 2 ends” << endl;
}


int main() {
way_1_with_lib_and_dll();
way_2_with_loadlibrary();

return 0;
}

此文件中使用了2种方法来调用MyDll.dll,即隐式调用和显式调用。最后将第二个工程设置为Startup工程,build后再按Ctrl + F5,就可以看到执行结果了。

小讯
上一篇 2025-05-02 16:15
下一篇 2025-04-19 23:25

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/169365.html