VS开发 - 静态编译和动态编译的基础实践与混用
目录
1. 基础概念
2. 直观感受一下静态编译和动态编译的体积与依赖项目
3. VS运行时库包含哪些主要文件(从VS2015起)
4. 动态库和静态库混用的情况
5. 感谢清单
1. 基础概念
所谓的运行时库(Runtime Library)就是WINDOWS系统给我们实现了C/C++的库函数的功能,提供的.LIB和.DLL文件。
其中.LIB是静态库,假如我们程序用.LIB去链接的话,意味着这个EXE里就包含了C/C++库函数的实现,运行的时候就不需要再依赖目标电脑上的运行库,方便拿到其他电脑跑。缺点呢,就是程序体积大。
.DLL则是动态库,选择动态链接的时候,EXE需要用到C/C++库函数的实现的时候,会去系统里面找相对应的DLL。多个程序也可以共享这一个DLL,带来的好处就是节省EXE的体积,但是拿到其他电脑去的话可能跑不起来,报错说:“找不到XXXXX.DLL”
我们用visual studio开发的时候,在项目属性里面可以看到有4种运行时库可选
运行库名称 | 链接方式 | 其他 |
MT | 静态 | |
MTd | 静态 | Debug版本 |
MD | 动态 | |
MDd | 动态 | Debug版本 |
2. 直观感受一下静态编译和动态编译的体积与依赖项目
用vs新建一个工程,简单写一个hello world的程序。
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World" << endl;
return 0;
}
然后分别用静态生成和动态生成:
由上面两个图片可以清晰地看出来:静态编译体积大,动态编译依赖多
3. VS运行时库包含哪些主要文件(从VS2015起)
库描述 | MT | MTD | MD | MDd |
通用C运行时库 | libucrt.lib | libucrtd.lib |
|
|
VC运行库 | libvcruntime.lib | libvcruntimed.lib |
|
|
C++标准库 | libcpmt.lib | libcpmtd.lib |
|
|
初始化CRT的代码库 | libcmt.lib | libcmtd.lib | msvcrt.lib | msvcrtd.lib |
参考链接:C 运行时 (CRT) 和 C++ 标准库 (STL) .lib 文件 | Microsoft Learn
仔细留意一下【初始化CRT的代码库】,为什么动态生成的依赖库只有lib没有dll,官网说它的dll就是【通用C运行时库】和【VC运行库】的合集。这里有点难理解,也不知道为什么要这么设计呢?希望前辈们多多指教一下。
4. 动态库和静态库混用的情况
假如我们现在EXE使用动态编译,但依赖一个静态编译而成的LIB,可以吗?
试试!首先自己编译一个lib。头文件如下:
#pragma once
void StaticHello(); // 定义一个函数
源文件如下:
#include "my_mtd_lib.h"
#include <iostream>
using namespace std;
// 实现头文件的函数
void StaticHello()
{
cout << "----------" << endl;
}
生成选项用调试版的的静态库MTd
接着在其他工程调用这个LIB
#include <iostream>
using namespace std;
#include "../my-mtd-lib/my_mtd_lib.h"
#pragma comment(lib, "../Debug/my-mtd-lib.lib")
int main()
{
cout << "**********" << endl;
StaticHello(); // 调用my-mtd-lib.lib的函数
return 0;
}
生成选项用调试版的动态库MDd
点生成报错:
1>libcpmtd.lib(wlocale.obj) : error LNK2005: "public: int __thiscall std::ios_base::flags(void)const " (?flags@ios_base@std@@QBEHXZ) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
1>libcpmtd.lib(wlocale.obj) : error LNK2005: "public: __int64 __thiscall std::ios_base::width(__int64)" (?width@ios_base@std@@QAE_J_J@Z) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
1>libcpmtd.lib(wlocale.obj) : error LNK2005: "public: __int64 __thiscall std::ios_base::width(void)const " (?width@ios_base@std@@QBE_JXZ) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
1>libcpmtd.lib(xlocale.obj) : error LNK2005: "public: int __thiscall std::ios_base::flags(void)const " (?flags@ios_base@std@@QBEHXZ) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
1>libcpmtd.lib(xlocale.obj) : error LNK2005: "public: int __thiscall std::basic_streambuf<char,struct std::char_traits<char> >::sputc(char)" (?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
1>libcpmtd.lib(xlocale.obj) : error LNK2005: "public: __int64 __thiscall std::ios_base::width(__int64)" (?width@ios_base@std@@QAE_J_J@Z) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
1>libcpmtd.lib(xlocale.obj) : error LNK2005: "public: __int64 __thiscall std::ios_base::width(void)const " (?width@ios_base@std@@QBE_JXZ) 已经在 msvcprtd.lib(MSVCP140D.dll) 中定义
报的都是同一个错:libcpmtd.lib与msvcprtd.lib冲突,有符号重定义。再仔细看是不是很眼熟,刚好是前面 第三部分 表格里面【C++标准库】里面MTd和MDd,跟我们项目的设定一一对应。我对这个报错的理解是:链接阶段,main.obj会找到msvcprtd.lib里面关于cout的符号,也发现my-mtd-lib.lib也带了来自libcpmtd.lib的cout的符号,所以报重定义。
所以一个项目里面像我的例子这样混用两个运行时库,实际上大概率不行,理论上也不好。最优的做法当然是所有依赖都基于同一套运行时库。
5. 感谢清单
这篇文章是受到下面博主的文章指引启发的,感谢前辈的分享
你所不知道的C和C++运行库_vcc++运行库-CSDN博客