静态库与动态库
静态库
特点
1. 文件扩展名
- 在 Windows 上:.lib(msvc),.a(mingw)
- 在 Unix/Linux 上:`.a`2. 链接方式
- 在编译时将库的代码直接嵌入到可执行文件中。3. 代码重用
- 多个可执行文件共享相同的库代码,但是每个可执行文件都有库代码的副本。4. 运行时独立性
- 静态链接的可执行文件在运行时不依赖库文件,所有代码都包含在可执行文件中。
优点
1. 独立性
- 一旦生成可执行文件,它不再依赖于外部库文件,便于分发和部署。
2. 性能
- 由于所有代码在编译时已经确定,加载速度更快,运行时没有动态链接的开销。3. 兼容性
- 避免了不同版本库之间的兼容性问题,确保了运行时的稳定性。
缺点
1. 文件大小
- 每个静态链接的可执行文件都包含库的副本,增加了文件大小。2. 更新困难
- 如果库有更新,需要重新编译所有依赖该库的可执行文件。
动态库
特点
1. 文件扩展名
- 在 Windows 上:`.dll`
- 在 Unix/Linux 上:`.so`2. 链接方式
- 在运行时将库文件动态加载到可执行文件的地址空间中。3. 代码重用
- 多个可执行文件共享相同的库文件,节省内存和磁盘空间。4. 运行时依赖性
- 可执行文件在运行时依赖于外部库文件。
优点
1. 节省资源
- 共享库文件,多个程序可以共享相同的库,节省磁盘空间和内存。2. 易于更新
- 库文件更新后,所有依赖该库的程序无需重新编译,只需确保新版本库的兼容性。3. 模块化
- 可以将应用程序分解为多个模块,易于管理和维护。
缺点
1. 依赖性
- 可执行文件在运行时依赖于外部库文件,如果库文件缺失或版本不兼容,程序将无法运行。2. 加载时间
- 由于在运行时需要动态加载库文件,启动速度可能稍慢。3. 版本控制
- 不同程序可能需要不同版本的库,容易引起“DLL地狱”问题,即版本冲突。
生成
动态链接(Dynamic Linking)是一种链接方式,在这种方式下,库文件在程序运行时而不是在编译时被加载。这些库文件被称为动态库或共享库(在 Windows 上称为 DLL,Dynamic Link Library;在 Unix/Linux 上称为 .so,Shared Object)。
静态链接(Static Linking)是在编译时将库文件的代码复制到每一个使用该库的可执行文件中。这种方式生成的库文件被称为静态库(在 Unix/Linux 上通常以 .a 结尾,在 Windows 上通常以 .lib 结尾)。
总结
选择使用静态库还是动态库,取决于具体的应用场景和需求,两者都有其优点和缺点
静态库:适用于需要独立部署、更新频率低、对启动速度和兼容性要求高的场景。
动态库:适用于模块化设计、需要节省资源、库文件更新频繁的场景。
延申
lib库实际上分为两种,一种是静态链接lib库或者叫做静态lib库(就是我们常说的静态库),另一种叫做动态链接库dll库的lib导入库或称为lib导入库。这两个库是不一样的,很多人都分不清楚,很容易混淆。下面我给你解释清楚,你就不会再糊涂了。
第一种是静态lib,包含了所有的代码实现的,是源代码文件.c或.cpp文件编译生成的,这个lib库就是文本形式的源代码编译后的二进制形式代码。
所以只要导入后,使用链接器链接生成exe文件后,那么exe文件就可以直接使用exe 内部的代码了。这个链接lib库的过程就相当于把lib库里的所有二进制的代码复制到exe文件中。所以,链接完后,静态lib库文件就不需要了。最后, 我们只要exe就行了。但是,每次编译链接生成exe时都需要这个静态lib库哦。这意思是说,最后你给别人的只需要给一个exe就行了,就不必将lib也给别人。在写代码时,我们要调用lib库里的函数,我们是通过提供的头文件来知道lib静态库里都有些什么函数的。
第二种就是lib导入库,这个库只是dll文件中的所有函数在dll文件中的地址的说明。
这个lib导入库可以说明dll的内部结构,简直就对dll内部了如指掌,我们通过lib导入库,就可以轻松调用到dll里面的函数。而我们在程序中使用 dll的时候,我们导入lib导入库之后,然后将dll放入项目中,就可以直接使用,就好像dll里的代码就和我们写的cpp源代码文件一样。所以非常方便,这也是lib导入库存在的理由了。而dll的使用,还可以直接使用API函数来获取dll内部的函数的地址,然后将函数类型转换为正确的函数类型。而这些函数类型声明就在提供的头文件中了。头文件和我们自己写的头文件一样的。这个过程呢,就非常麻烦了,这才有上面那个lib导入库的存在的空间了。同样,lib导入库在编译链接后,那么dll里的函数地址结构等信息也都复制到exe里面了。所以,最后生成程序之后,lib导入库和静态lib一样的命运,那就是被抛弃了。哎,现实已经很残酷了,在计算机里,更是残酷。
MinGW 编译与MSVC编译
在项目文件(.pro)中,可以通过指定CONFIG
变量来指定是生成动态库还是静态库。
MinGW 编译
1. 动态链接
在Qt中默认生成的就是动态库
2. 静态链接
- 要静态链接库文件,可以在pro文件中添加CONFIG += staticlib
MSVC 编译
1. 动态链接
在Qt中默认生成的就是动态库
2. 静态链接
- 要静态链接库文件,可以在pro文件中添加CONFIG += staticlib
不同点
1. 工具链
- MinGW 使用 GNU 工具链,MSVC 使用微软的 Visual Studio 工具链。2. 库文件
- MinGW 通常使用 .a 文件,MSVC 使用 .lib 文件。3. 性能和二进制兼容性
- MSVC 编译的程序在 Windows 上可能有更好的优化和兼容性,特别是在与其他 MSVC 编译的库交互时。4. IDE 支持
- MinGW 通常与 Qt Creator 一起使用,而 MSVC 常与 Visual Studio 一起使用,但也可以在 Qt Creator 中配置使用。
MinGW 下编译动态库与静态库
动态库
第一步:打开Qt Creater,新建库项目
自己起个库名,最好有大小写,要不然后面可能报错
第二步:选择库类型,选择依赖
第三步:选择mingw
完成后可以看下pro文件内容
第四步:为库添加内容
FirstDll.h
#ifndef FIRSTDLL_H
#define FIRSTDLL_H
#include "FirstDll_global.h"
// DllMain() 是动态链接库(DLL)的入口点函数,类似于构造函数。
// DllMain 主要处理 DLL 模块的初始化和清理工作,响应系统对 DLL 的加载、卸载等事件。
// DllMain 通常不在 .h 文件中声明,而是在 .cpp 文件中定义。
class FIRSTDLL_EXPORT FirstDll
{
public:
FirstDll();
int add(int a, int b);
void print();
bool isFalse;
};
// 在动态库中定义的函数和变量可以定义在类外部,也可以定义在类内部。
// 这里的 extern "C" 用于防止 C++ 编译器对函数名进行“名字修饰”(name mangling),
// 使得函数可以被 C 语言或其他不支持 C++ 名字修饰的语言调用。
/*************************************************************
extern "C"
在C++中,函数名在编译时会被“名字修饰”(name mangling),
这是为了支持函数重载等C++特性。然而,
这种名字修饰会使得C++编译的函数名与C编译器产生的函数名不同,
从而导致链接错误。当你希望C++库能够被C程序(或其他不支持C++名字修饰的语言)调用时,
你需要使用 extern "C" 来告诉编译器以C语言的方式处理这些函数,不进行名字修饰。
MYDLL_EXPORT
MYDLL_EXPORT 是一个宏,它根据当前是构建库(定义 MYDLL_LIBRARY)还是使用库(未定义 MYDLL_LIBRARY)
来解析为 Q_DECL_EXPORT 或 Q_DECL_IMPORT。这允许库在构建时导出符号,而在其他程序中使用库时导入这些符号。
***********************************************************/
extern "C" FIRSTDLL_EXPORT int sub(int a, int b);
#endif // FIRSTDLL_H
FirstDll.cpp
#include "firstdll.h"
#include <iostream>
FirstDll::FirstDll()
{
isFalse = false;
}
int FirstDll::add(int a, int b)
{
return a + b;
}
void FirstDll::print()
{
if (isFalse) {
std::cout<<"result:"<<isFalse<<"\n"
isFalse = !isFalse;
} else {
std::cout<<"result:"<<isFalse<<"\n"
isFalse = !isFalse;
}
}
int sub(int a, int b)
{
return a - b;
}
第五步:选择release,编译项目
第六步:右键任意文件,在explorer中显示
点击release文件夹
ok
静态库
第一步:打开Qt Creater,新建库项目
第二步:选择库类型,选择依赖
因为就做一个简单的例子,就不依赖Qt的模块了
第三步:选择mingw
完成后可以看下pro文件内容
第四步:为库添加内容
FirstLib.h
#ifndef FIRSTLIB_H
#define FIRSTLIB_H
class FirstLib
{
public:
FirstLib();
int add(int a, int b);
void print();
private:
bool isFalse;
};
#endif // FIRSTLIB_H
FirstLib.cpp
#include "firstlib.h"
#include <iostream>
FirstLib::FirstLib()
{
isFalse = false;
}
int FirstLib::add(int a, int b)
{
return a + b;
}
void FirstLib::print()
{
if (isFalse) {
std::cout<<"result:"<<isFalse<<"\n";
isFalse = !isFalse;
} else {
std::cout<<"result:"<<isFalse<<"\n"
isFalse = !isFalse;
}
}
第五步:选择release,编译项目
第六步:右键任意文件,在explorer中显示
点击release文件夹
完成
MSVC下 编译动态库与静态库
动态库
第一步:打开Qt Creater,新建库项目
第二步:选择库类型,选择依赖
这里选则动态库,因为仅作测试使用,就不选择依赖了
第三步:选择msvc
完成后可以查看下pro文件内容
第四步:为库添加内容
SecondDll.h
#ifndef SECONDDLL_H
#define SECONDDLL_H
#include "SecondDll_global.h"
class SECONDDLL_EXPORT SecondDll
{
public:
SecondDll();
int add(int a, int b);
void print();
bool isFalse;
};
// 在动态库中定义的函数和变量可以定义在类外部,也可以定义在类内部。
// 这里的 extern "C" 用于防止 C++ 编译器对函数名进行“名字修饰”(name mangling),
// 使得函数可以被 C 语言或其他不支持 C++ 名字修饰的语言调用。
/*************************************************************
extern "C"
在C++中,函数名在编译时会被“名字修饰”(name mangling),
这是为了支持函数重载等C++特性。然而,
这种名字修饰会使得C++编译的函数名与C编译器产生的函数名不同,
从而导致链接错误。当你希望C++库能够被C程序(或其他不支持C++名字修饰的语言)调用时,
你需要使用 extern "C" 来告诉编译器以C语言的方式处理这些函数,不进行名字修饰。
MYDLL_EXPORT
MYDLL_EXPORT 是一个宏,它根据当前是构建库(定义 MYDLL_LIBRARY)还是使用库(未定义 MYDLL_LIBRARY)
来解析为 Q_DECL_EXPORT 或 Q_DECL_IMPORT。这允许库在构建时导出符号,而在其他程序中使用库时导入这些符号。
***********************************************************/
extern "C" SECONDDLL_EXPORT int sub(int a, int b);
#endif // SECONDDLL_H
SecondDll.cpp
#include "seconddll.h"
#include <iostream>
SecondDll::SecondDll()
{
isFalse = false;
}
int SecondDll::add(int a, int b)
{
return a + b;
}
void SecondDll::print()
{
if (isFalse) {
std::cout<<"result:"<<isFalse<<"\n";
isFalse = !isFalse;
} else {
std::cout<<"result:"<<isFalse<<"\n";
isFalse = !isFalse;
}
}
int sub(int a, int b)
{
return a - b;
}
第五步:选择release,编译项目
第六步:右键任意文件,在explorer中显示
点击release文件夹
完成
静态库
第一步:打开Qt Creater,新建库项目
第二步:选择库类型,选择依赖
第三步:选择msvc
pro文件内容
第四步:为库添加内容
SecondLib.h
#ifndef SECONDLIB_H
#define SECONDLIB_H
class SecondLib
{
public:
SecondLib();
int add(int a, int b);
void print();
private:
bool isFalse;
};
#endif // SECONDLIB_H
SecongLib.cpp
#include "secondlib.h"
#include <iostream>
SecondLib::SecondLib()
{
isFalse = false;
}
int SecondLib::add(int a, int b)
{
return a + b;
}
void SecondLib::print()
{
if (isFalse) {
std::cout<<"result:"<<isFalse<<"\n";
isFalse = !isFalse;
} else {
std::cout<<"result:"<<isFalse<<"\n";
isFalse = !isFalse;
}
}
第五步:选择release,编译项目
第六步:右键任意文件,在explorer中显示
点击release文件夹
完成
使用
库文件的使用参照我另一篇博客:Qt中外部库的使用(MinGw与MSVC)