静态链接库与动态链接库

静态链接库

何为静态链接库?

win平台下xxx.lib文件即静态链接库。静态库只有lib文件,该文件包含了函数代码本身,在编译时会直接将代码完整添加到程序之中。

使用VS2019创建一个静态库项目

在这里插入图片描述
在这里插入图片描述
选择静态库,在下面选项中勾选空项目

在这里插入图片描述
testlib.h

#ifndef TESTLIB_H
#define TESTLIB_H

int add(int a, int b);

#endif

testlib.cpp

#include "testlib.h"

int add(int a, int b) 
{
    
    
	return a + b;
}

生成解决方案并生成静态库

在这里插入图片描述
接下来尝试使用静态库,在同一解决方案下新建项目test,并右击将其设置为启动项目

在这里插入图片描述
test.cpp

#include <stdio.h>

int main()
{
    
    
	int a, b = 0;

	scanf_s("%d %d", &a, &b);

	int c = add(a, b);

	printf("%d", c);

	return 0;
}

直接编译,报错

在这里插入图片描述
那是因为程序不知道add函数是什么,或者说不知道去哪里调用

包含一下静态库项目中的头文件testlib.h

#include <stdio.h>
#include "../testlib/testlib.h"

int main()
{
    
    
	int a, b = 0;

	scanf_s("%d %d", &a, &b);

	int c = add(a, b);

	printf("%d", c);

	return 0;
}

可通过编译,但运行继续报错

在这里插入图片描述
那是因为头文件中只有声明,没有实现。因此,可以引出静态库如何调用的问题

静态库的调用需要两个文件:xxx.hxxx.lib

方式一:将testlib.lib文件复制至test项目工作目录,并添加附加依赖项

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include "../testlib/testlib.h"

// #pragma comment(lib, "testlib.lib")

int main()
{
    
    
	int a, b = 0;

	scanf_s("%d %d", &a, &b);

	int c = add(a, b);

	printf("%d", c);

	return 0;
}

方式二:test项目中添加静态库的库目录相对路径,并在程序中添加导入对应库的代码

在这里插入图片描述

#include <stdio.h>
#include "../testlib/testlib.h"

#pragma comment(lib, "testlib.lib")

int main()
{
    
    
	int a, b = 0;

	scanf_s("%d %d", &a, &b);

	int c = add(a, b);

	printf("%d", c);

	return 0;
}

方式三:test项目中添加静态库的库目录相对路径,并在程序中添加附加依赖项

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include "../testlib/testlib.h"

// #pragma comment(lib, "testlib.lib")

int main()
{
    
    
	int a, b = 0;

	scanf_s("%d %d", &a, &b);

	int c = add(a, b);

	printf("%d", c);

	return 0;
}

可见,无论是哪种方式,都要使test项目找得到静态库。实际开发中方式二使用居多。

方式三与方式二的区别在于:方式二使用预处理指令导入库文件;而方式三通过设置工程属性导入库文件。

#pragma comment(lib, "XXX.lib")

动态链接库

windows操作系统有三个核心的动态链接库:

  • kernel32.dll

kernel32.dllWindows中非常重要的32位动态链接库文件,属于内核级文件。它控制着系统的内存管理、数据的输入输出操作和中断处理,当Windows启动时,kernel32.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域。

Windows系统中,每个.exe文件在双击打开时都会加载kernel32.dll这个系统模块,该模块中有一个LoadLibrary()函数,可以将DLL文件加载到自身进程中。可以用CreateRemoteThread()函数创建一个远程线程,让目标进程调用LoadLibrary()来加载我们自己写的DLL

  • user32.dll

user32.dllWindows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息。

  • gdi32.dll

gdi32.dllWindows GDI图形用户界面相关程序,包含的函数用来绘制图像和显示文字

何为动态链接库?

win平台下xxx.dll即为动态链接库。动态链接库不会将完整代码拷贝进xxx.exe文件,仅仅会在程序运行的时候,才会将具体的代码加载进去。

使用VS2019创建一个动态库项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
testdll.h

#ifndef TESTDLL_H
#define TESTDLL_H

#ifdef _DLLAPI
	#define DLLAPI _declspec(dllexport)
#else
	#define DLLAPI _declspec(dllimport)
#endif

int DLLAPI add(int a, int b);

#endif

这里使用条件编译指令是更加规范的做法,可以让testdll项目和测试项目test共用一个头文件testdll.h

设置testdll项目的预处理器中添加宏_DLLAPI

在这里插入图片描述
如此一来,可以使得在生成dll文件时将DLLAPI替换成_declspec(dllexport),而在测试项目中导入头文件时,将DLLAPI替换成_declspec(dllimport)

testdll.cpp

#include "testdll.h"

int add(int a, int b)
{
    
    
	return a + b;
}

右击项目,重新生成解决方案并生成动态库(注意,dll项目不能调试,点击调试会报错的)

在这里插入图片描述

在这里插入图片描述

此时,我们去下载一个工具 dllexp,打开软件并导入testdll.dll,可见函数名被修饰了

在这里插入图片描述

接下来尝试使用动态库,在同一解决方案下新建项目test,并右击将其设置为启动项目

动态库调用有两种方式:静态调用动态调用

1. 静态调用

静态调用需要三个文件:xxx.hxxx.libxxx.dll

使用相对路径设置test项目的库目录路径

在这里插入图片描述

test.cpp

#include <stdio.h>
#include "../testdll/testdll.h"

#pragma comment(lib, "testdll.lib") //这个不是真正的静态库

int main()
{
    
    
	int a = 1;
	int b = 2;

	int c = add(a, b);

	printf("%d\n", c);

	return 0;
}

为什么不需要如网上其他帖子所说:在调用dll文件时,需要xxx.hxxx.libxxx.dll缺一不可。而我这里没有将dll文件复制到test项目的可执行目录中,是因为他们俩同处在一个解决方案下面。

在这里插入图片描述
但是神奇的是,test.exe可执行文件不在原项目test/Debug中,而是跑到了testdll/Debug中。纵然满足exedll文件在同一目录下,但仍然令我费解。

在这里插入图片描述

在这里插入图片描述
2. 动态调用

动态调用只要一个文件:xxx.dll

由于testdll项目中是用cpp文件写的,即以C++的方式导出动态库的函数,那么编译器为了实现C++的函数重载会在函数导出时对函数名进行修饰。因此,修改一下testdll.h文件。

#ifndef TESTDLL_H
#define TESTDLL_H

#ifdef _DLLAPI
#define DLLAPI _declspec(dllexport)
#else
#define DLLAPI _declspec(dllimport)
#endif

// extern "C" 编译时用C的编译方式
extern "C" DLLAPI int add(int a, int b);

#endif

重新生成解决方案后的dll文件中add函数名就不会被修饰

在这里插入图片描述

test.cpp

#include <stdio.h>
#include <Windows.h>
//#include "../testdll/testdll.h"

//#pragma comment(lib, "testdll.lib") //这个不是真正的静态库

typedef int (*pAdd)(int a, int b);

int main()
{
    
    
	HMODULE hDll = LoadLibrary(L"../testdll/Debug/testdll.dll"); //加载库文件

	if (hDll == NULL)
	{
    
    
		printf("加载DLL文件失败\n");
		return 0;
	}

	pAdd testAdd = (pAdd)GetProcAddress(hDll, "add");

	int a = 1;
	int b = 2;

	printf("add(a, b) = %d\n", testAdd(a, b));

	FreeLibrary(hDll);

	return 0;
}

成功运行

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Star_ID/article/details/125394726