VS开发 - 静态编译和动态编译的基础实践与混用

目录

[1. 基础概念](#1. 基础概念)

[2. 直观感受一下静态编译和动态编译的体积与依赖项目](#2. 直观感受一下静态编译和动态编译的体积与依赖项目)

[3. VS运行时库包含哪些主要文件(从VS2015起)](#3. VS运行时库包含哪些主要文件(从VS2015起))

[4. 动态库和静态库混用的情况](#4. 动态库和静态库混用的情况)

[5. 感谢清单](#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的程序。

cpp 复制代码
#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 | ucrt.lib ucrtbase.dll | ucrtd.lib ucrtbased.dll |
| VC运行库 | libvcruntime.lib | libvcruntimed.lib | vcruntime.lib vcruntime<version>.dll | vcruntimed.lib vcruntime<version>d.dll |
| C++标准库 | libcpmt.lib | libcpmtd.lib | msvcprt.lib msvcp<version>.dll | msvcprtd.lib msvcp<version>d.dll |
| 初始化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。头文件如下:

cpp 复制代码
#pragma once

void StaticHello(); // 定义一个函数

源文件如下:

cpp 复制代码
#include "my_mtd_lib.h"
#include <iostream>
using namespace std;

// 实现头文件的函数
void StaticHello()
{
	cout << "----------" << endl;
}

生成选项用调试版的的静态库MTd

接着在其他工程调用这个LIB

cpp 复制代码
#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

点生成报错:

cpp 复制代码
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.libmsvcprtd.lib冲突,有符号重定义。再仔细看是不是很眼熟,刚好是前面 ++第三部分++ 表格里面【C++标准库】里面MTd和MDd,跟我们项目的设定一一对应。我对这个报错的理解是:链接阶段,main.obj会找到msvcprtd.lib里面关于cout的符号,也发现my-mtd-lib.lib也带了来自libcpmtd.lib的cout的符号,所以报重定义。

所以一个项目里面像我的例子这样混用两个运行时库,实际上大概率不行,理论上也不好。最优的做法当然是所有依赖都基于同一套运行时库。

5. 感谢清单

这篇文章是受到下面博主的文章指引启发的,感谢前辈的分享

你所不知道的C和C++运行库_vcc++运行库-CSDN博客

相关推荐
4ever.ov01 分钟前
速通数据结构与算法第七站 排序
c语言·数据结构·算法·visualstudio·排序算法
GEEKVIP36 分钟前
升级 Windows 后如何恢复丢失的文件
android·windows·安全·macos·智能手机·电脑·笔记本电脑
会编程的果子君1 小时前
C++系列-继承
开发语言·c++·算法
打工小熊猫1 小时前
如何在CMakeList项目中集成GNU Autotools 构建模块
c++·gnu
Am心若依旧4091 小时前
[c++高阶]模版进阶
开发语言·c++
windxgz2 小时前
FFMPEG总结——底层调用COM导致编码器无法正常打开
c++·qt·ffmpeg
东方冷哥2 小时前
考研数据结构——C语言实现归并排序
c语言·数据结构·考研
TravisBytes2 小时前
Protobuf 为什么这么快?解密它背后的高效编码机制与 C++ 实践
c++·分布式·go
"Return"2 小时前
小红书2024秋招后端开发(Java工程师、C++工程师等)
java·开发语言·c++
绯樱殇雪2 小时前
编程题 7-14 求整数段和【PAT】
c++·pat考试