[C-C++]你所不知道的C和C++运行库
原创文章,欢迎转载。转载请注明:转载自 祥的博客
原文:你所不知道的C和C++运行库
转载后只做了格式上的编辑,原文如下:
周五晚,小雨,少见的未加班。无聊,遂准备写一篇博客,介绍一下C和C++运行库,只因发现工作几年的人对此一知半解的大有人在。
在使用VC构建项目时,经常会遇到下面的链接错误:
初学者面对这些错误常常不知所错:libcmt.lib
是什么东西?msvcrtd.dll
又是干吗用的?在使用VC++
时我们也常常对下面的运行库配置项感到疑惑,它们到底究竟是什么意思呢?甚至一些工作了很多年的程序员也对此一知半解。今天让我们来了解一下它们。
从C和C++运行库说起
为了提高C语言的开发效率,C标准定义了一系列常用的函数,称为C库函数
。C标准仅仅定义了函数原型,并没有提供实现。因此这个任务留给了各个支持C语言标准的编译器
。每个编译器通常实现了标准C的超集
,称为C运行时库(C Run Time Libray)
,简称CRT
。对于VC++编译器
来说,它提供的CRT库支持C标准定义的标准C函数,同时也有一些专门针对windows系统特别设计的函数。
与C语言类似,C++也定义了自己的标准,同时提供相关支持库,我们把它称为C++运行时库
或C++标准库
。
由于C++对C的兼容性
,C++标准库
包括了C标准库
,除此之外还包括IO流
和标准模板库STL
。
VC++在何处实现C和C++运行库
VC++
完美的支持C和C++标准
,因此也就按照C和C++的标准定义的函数原型
实现了上述运行时库
。为了方便有不同需求的客户的使用,VC++
分别实现了动态链接库DLL版本
和静态库LIB版本
。同时为了支持程序调试且不影响程序的性能,又分别提供了对应的调试版本。调试版本的名称在Release版本
名称后添了字母d
。
对于C运行时库CRT
,VC6.0
、VC2005
、VC2008
和VC2010
均提供了DLL版本
和LIB版本
。上述各个编译器提供的LIB版的CRT库
,均实现在libcmt.lib
。对应的调试版名称为libcmtd.lib
。
而DLL版本名称根据编译器不同而不同,我们可以从名称上加以分辨:
-
VC6.0
使用的CRT库
的DLL版本在MSVCRT.DLL
中实现, 对应调试版本为MSVCRTD.LIB
。 -
VC2005
使用的CRT库
的DLL版本在MSVCR80.DLL
中实现,对应调试版本为MSVCR80.DLL
。 -
VC2008
使用的CRT库
的DLL版本在MSVCR90.DLL
中实现,对应调试版本为MSVCR90D.DLL
。 -
VC2010
使用的CRT库
的DLL版本在MSVCR100.DLL
中实现,对应调试版本为MSVCR100D.DLL
。
C++标准兼容C标准
,但VC各版本
将C++编译器
使用的C标准库与C编译器
使用的C运行库
一起实现,它们使用相同的运行库
。
对于C++标准库
中的IO流
和STL,VC6.0、VC2005、VC2008和VC2010
也提供了DLL版本
和LIB版本
。
LIB版
均实现在libcpmt.lib
中,对应的调试版本为libcpmtd.lib
。
不同版本的编译器实现的DLL也不相同:
-
VC6.0
使用的C++类库
的 DLL版本在MSVCP60.DLL
中实现, 对应调试版本为MSVCP60D.LIB
。 -
VC2005
使用的C++类库
的DLL版本在MSVCP80.DLL
中实现,对应调试版本为MSVCP80.DLL
。 -
VC2008
使用的C++类库
的 DLL版本在MSVCP90.DLL
中实现,对应调试版本为MSVCP90D.DLL
。 -
VC2010
使用的C++类库
的DLL版本在MSVCP100.DLL
中实现,对应调试版本为MSVCP100D.DLL
。
在各个版本的编译器中,我们可以通过配置选项来设置程序使用的C和C++运行时库
的类型。如下图(其他版本编译器大同小异):
-
MT选项
:链接LIB版的C和C++运行库
。在链接时就会在将C和C++运行时库
集成到程序中
成为程序中的代码
,程序体积会变大。 -
MTd选项
:LIB的调试版
。 -
MD选项
:使用DLL版的C和C++运行库
,这样在程序运行时会动态的加载对应的DLL
,程序体积会减小,缺点是在系统没有对应DLL时程序无法运行
。 -
MDd选项
:表示使用DLL的调试版
。
在《由使用LeakDialog时遇到的问题而引出的一些分析》这篇文章中的实验一,使用VC6.0
的默认配置没有拦截到内存泄露。其原因是VC6.0
的控制台项目默认配置是静态链接CRT库
(单线程版,后面会介绍)。
动态版(DLL)和静态版(LIB)C和C++运行库的优缺点
因为静态版
必须把C和C++运行库
复制到目标程序
中,所以产生的可执行文件会比较大
。同时对于使用多个模块
的大型软件
来说,如果每个模块
均选择静态链接C或C++运行库
,在程序运行时就会存在多个运行库
。在链接时也会出现重复定义的问题
,如文章首第一张图所示。
使用DLL版的C和C++运行库
,程序在运行时动态的加载对应的DLL
。程序体积变小,但一个很大的问题就是一旦找不到对应DLL,程序将无法运行。假设使用VC6.0
并选择使用MD选项构建
,那么当用户使用VC2005
来使用这个DLL
时很可能出现找不到MSVCRT.DLL
或MSVCP60.DLL
的情况。
在这里介绍一个很好的工具:Dependency Walker
,可以用来分析DLL的依赖关系
,同时查看DLL导出的函数
,使用方法请Google。
使用该工具打开MSVCRT.DLL
,如下图:
我们可以在其中找到我们经常使用使用的C函数
,如printf
,getchar
,malloc
等。
打开MSVCP100.DLL
,也可以找到这些C函数
:
在开发的过程中我们也会遇到如下图的链接错误,LIBCD.lib
究竟是何方神圣呢?
它其实是LIBC.lib
的调试版,而LIBC.lib
是只有在VC6.0
才会使用的静态库,该库是CRT的单线程版
,用于支持单线程版本的CRT
。VC2005
等更高版本的编译器已经不再提供单线程版本,转而使用多线程版的MSVCR80.DLL
或libcmt.lib
。
当遇到上述符号定义冲突的链接错误时,可以选择忽略libcd.lib
。
2014.2.28 于浙江杭州
本文地址:https://blog.csdn.net/humanking7/article/details/85887884
上一篇: 开关电源EMI设计(原理讲解及实战分析)
下一篇: 常引用