10. Windows静态库与动态库制作指南

1.前言

前面我们一直提到静态库与动态库,但都只是大致说了一下,并没有过于深入的讲解。

但随着后面的C/C++学习中,你将会遇到大量的关于动态库与静态库的使用,因为各种第三方库基本都是会提供静态库或动态库的。

所以本章将详尽介绍一下在Windows平台动态库与静态库的制作流程。

请注意,不同平台的静态库与动态库并不相同,需要各自单独制作。

2.静态库

前面已经说过了静态库的作用,其主要目的是为了便于使用以及不泄露源码,很大程度上都是等价的替换了所有的源文件(.cpp),所以使用的时候我们仍然需要头文件(.h)。

还是老规矩,先建立项目,不过这次不同,得建立三个!分别为静态库项目动态库项目以及可执行程序项目

先建立个控制台的可执行项目,这就不多说了,名称就叫 day10-exe了。

这里主要讲解一下建立静态库与动态库项目的过程:

image-20231206182821917

名称就分别叫day10-staticday10-dynamic即可。

image-20231206182947057

动态库的项目就先不管了,后面再说,我们先来看静态库,其默认生成了下面这些文件:

image-20231206183014436

先来说说这些文件的作用:

  • pch : 预编译文件

所谓预编译,其意思就是把不会更改的源代码先行编译了,此后就可以直接使用,比如C++的iostream,windows的windows.h等等,我们不可能去改它们的源码,那就可以把它们包含进pch.h中,以后如果我们想使用这些标准库文件,就只需要包含pch.h即可。

不过要注意,这些官方的库一般都是已经被编译过了的,所以这样做并没有太大的用处,非要说的话,可以让我们只包含一个pch.h就能间接包含pch.h中的所有头文件,可以方便我们使用。

其最大的用处其实是对我们自己写的项目代码,有些代码文件功能是一次写好很久都不会动的,那就可以将其包含进来。

所起的作用是这些源文件将只在第一次运行时被编译,只要你后面不更改这些源文件,那就不会再编译,这可以大大减少我们编译程序的时间。

  • framework:一个框架文件,一般主要用于包含一些宏之内的东西。

可以看到pch文件的官方说明:

image-20231206183115132

其中就包含了framework.h文件。

这里还有一个宏的特殊用法:

#ifndef PCH_H
#define PCH_H

#endif //PCH_H
  1. ifndef :if not define,即如果没有定义后面这个宏。
  2. define :那我就定义这个宏
  3. endif :结束标志

ifndef 是一种宏用法,除了它,还有ifdef等,看名字也知道它是什么意思了,可以用来判断紧跟其后的这个宏被定义了没。

这个宏的名字是可以随便写的,但为了防止与其它文件定义的宏重名,所以一般就会采用本文件名大写、后面跟着后缀_H来作为宏的名字,字母H代表这是头文件(Header)。

它就和if判断语句一样,只有满足了条件才会执行里面的内容。

比如这里,只有以前没有定义过PCH_H宏,其里面包含的头文件才会被包含进来,并同时将这个宏定义了。

这是为了避免重复包含,比如这里包含了两次

#include<pch.h>
#include<pch.h>

但展开后其实是:

#ifndef PCH_H
#define PCH_H
//头文件
#endif //PCH_H

#ifndef PCH_H
#define PCH_H
//头文件
#endif //PCH_H

这样就只包含了上面一段,下面这段就被编译器给忽略了,因为上面那段已经定义了宏。

如果项目里面使用了预编译文件,比如这里的pch,那么你写的所有其它源文件(.cpp)都必须添加pch.h头文件,否则会编译失败!

当然,这些文件并不是必要的,如果你不关心编译时间,完全可以删除:

image-20231206183351417

这里我将自动生成的其它三个文件都删除了,并添加了一个day10-static.h的头文件,用做函数声明。

然后我们就可以向平常一样,写函数即可,比如写一个加法函数:

image-20231206183446453

设置静态库项目为启动项,编译试一试:

image-20231206183530332

正如前面所说,如果使用了预编译,那么就必须要在源文件中包含它,虽然我们已经删除了该文件,但设置里面还没关闭使用源文件。

来到该项目的属性界面,设置一下即可:

image-20231206183641987

注意项目属性设置页面中,上面的配置要和你项目配置相同,即都是Debugx64模式,现在返回编译就可以了。

不过由于静态库以及后面要介绍的动态库本身都是无法运行的,所以你点击上方的“本地WIndows调试器”、或者“运行”,都会报错,因为它本来就无法被运行。

不过也无所谓,因为运行的第一步就是编译,所以是不用管这个报错信息的。

编译后,找到对应的头文件与lib文件:

image-20231206183942649

image-20231206183909994

将上面两者复制到我们前面创建的exe项目中:

在这里插入图片描述

然后在exe项目中,添加main.cpp文件,设置为启动项目,写上下面的代码:

#include<iostream>
using namespace std;
#include"day10-static.h"
#pragma comment(lib,"day10-static.lib")

int main() {
	cout << add(1, 3) << endl;
}

然后运行一下试一试:

image-20231206184152047

静态库的使用是不是很简单?

当然,一开始使用,可能会遇到各种各样的问题,这时候可以去浏览器搜索解决,或者询问deepseek之类的大模型。

3.动态库

动态库就和静态库有点不一样了。

通过前面的实验可以发现,静态库的作用其实就是替换了一下源文件(.cpp)而已,生成的程序与我们正常用.cpp文件编译的程序没有任何区别。

而动态库可完全不是这样,它主要生成两个文件,一个.lib文件和一个.dll文件。

这里的.lib文件与上面我们使用静态库生成的lib文件是一样的,只是这个里面并没有包含源代码,而只是包含了一些关于代码的基本信息。

比如函数在dll中的地址等,所以体积会比静态库项目生成的lib文件要小很多,因为其实际的代码编译到了相应的.dll文件中。

各个文件之间的关系就是,我们通过头文件知道这个动态库里面有些什么东西,编译器通过lib文件知道各个函数或其它信息在dll文件中的哪个地方,然后进行调用。

注意动态库生成的.dll文件,必须要跟着.exe文件走,否则.exe文件无法启动!

这就是动态库的缺点与优点,麻烦,但生成的.exe文件小,所以我们常常看到一些软件安装目录里面有一大堆dll文件,就是为了减少生成exe文件的大小,同时方便升级,比如偶尔只需要升级某个dll即可。

不好理解还是直接来实战吧,现在将动态库项目设置为启动项:

image-20231206184434146

作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux