ZZmsvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义 .的分析解决办法

ZZmsvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义 .的分析解决办法,第1张

很久没有写程式设计入门知识的相关文章了,这篇文章要来谈谈程式库 (Library) 连结,以及关于 MSVC 与 CRT 之间的种种恩怨情仇。

如果你使用的作业系统是 Linux、Mac 或其他非 Windows 平台,你可以忽略这篇文章;如果你使用的作业系统是 Windows 平台,但没有用 Microsoft Visual Studio C++(以下简称为 MSVC)软体撰写 C++ 程式的话,这篇文章对你的帮助可能很有限;但如果你的作业系统是 Windows,而且你使用的程式整合开发环境是 MSVC 软体撰写 C++ 程式的话,这篇文章应该能够帮助你釐清一些重要的基础观念。

身为程式设计者,在学习程式设计的过程中,你是否曾经遇过某些看起来不知所云的错误讯息,却不知该如何解决?例如当你快快乐乐地写完程式,并且确认所有的程式码都能成功通过编译之后,接着执行「建置方案」(Build Solution) 的步骤,结果却跑出一堆莫名其妙的错误:

LIBCMTD.lib(mlock.obj) : error LNK2005: __lock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定义过了
LIBCMTD.lib(mlock.obj) : error LNK2005: __unlock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定义过了
LIBCMTD.lib(crt0.obj) : error LNK2005: _mainCRTStartup 已在 MSVCRTD.lib(crtexe.obj) 中定义过了

…………

LINK : warning LNK4098: 预设的程式库 ‘MSVCRTD’ 与其他使用的程式库冲突,请使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 预设的程式库 ‘LIBCMTD’ 与其他使用的程式库冲突,请使用 /NODEFAULTLIB:library
D:\Workspace\CrtLibTest\Debug\CrtLibTest.exe : fatal error LNK1169: 找到有一或多个已定义的符号

以一般的情况来说,如果在你的程式专案中有使用某些由他人所撰写的第三方程式库或是开源专案的程式库,比较容易会发生上述的错误状况。从上述这些看似离奇而令人摸不着头绪的错误讯息中,我们大概可以猜测问题点应该在于 LIBCMTD.lib 与 MSVCRTD.lib 这两个程式库身上。但到底什么是 LIBCMTD.lib 和 MSVCRTD.lib?在我们的程式码中有使用这些程式库吗?

答案是肯定的。

熟悉 C 语言的程式设计者都知道,如果要使用 printf()、scanf() 或者 fopen() 等等 C 语言的基本 I/O *** 作函式时,首先必须用 #include 语法将 stdio.h 这个标头档纳入我们的程式码中。藉由 stdio.h 中对这些 I/O *** 作函式所做出的函式宣告 (function declaration),编译器 (Compiler) 才得以确认 printf、scanf 以及 fopen 等等都是合法可用的函式。

而当我们撰写的程式码经过编译器产出 OBJ 形式的档案之后,需要再经由连结器 (Linker) 的处理程序,将程式码中全部有使用到的函式定义 (function definition) 连结建置起来,才能够产生出最后的程式执行档。问题来了,我们知道 printf、scanf 以及 fopen 的函式宣告存在于 stdio.h 当中,但是这些傢伙的函式定义,也就是真正的实做程式码,究竟存放在什么地方呢?

在 C 语言的标准程式库中。

由 C 语言所制订的标准程式库,称之为「执行阶段程式库」,也就是 C Run-Time Library,通常可简称为 CRT。在 C 语言的标准程式库中,包含了一组常用的基础函式,例如 I/O 处理与字串 *** 作程序等等,所以只要我们使用 C 语言撰写程式码,就一定要将编译完成后的程式码 OBJ 档,连结至 C 语言的执行阶段程式库,才能够产生出合法的 C 语言程式执行档。

而 CRT 并非只有单一一种版本存在。事实上,除了可以依「除错」与「释出」用途分成两个版本之外,两者又可分别衍生分出「静态连结」与「动态连结」两种形式:

静态连结

  • LIBCMTD.lib(除错版本)
  • LIBCMT.lib

动态连结

  • MSVCRTD.lib(除错版本)
  • MSVCRT.lib

虽然这四个 CRT 版本的用途与使用方式各不相同,但却有个共通的特点,就是它们都是满足执行绪安全需求,可在多执行绪程式码中安全使用的程式库版本。事实上,在过去 MSVC 6 的版本中,本来还有另外两个 LIBCD.lib(除错版本)与 LIBC.lib 程式库,是专门给单执行绪程式使用的 CRT 版本,但是这两个选项自 MSVC 2005 开始就从设定选项中被删除掉了,所以现在大多数程式设计者使用的都是多执行绪的 CRT 版本。

在程式库连结 (library linking) 的行为中,静态连结和动态连结的分别,在于使用静态连结时,会直接将程式库的函式定义嵌入执行档之中,而使用动态连结时,程式库的函式定义则存在于另外的独立档案,通常是 DLL 格式的档案中,然后与程式执行档一同发佈给使用者。因此在档案的尺寸上,使用动态连结的执行档档案,通常会比使用静态连结的执行档档案来得更小一些。

使用动态连结 CRT 版本的好处,是能够将经常使用到的标准程式库们独立出来,放在 Windows 的系统资料夹中,以减少我们建置出来的执行档档案尺寸。但反过来说,使用动态连结 CRT 版本的缺点也在于这些与执行档相依为命的 DLL 档案上。举例来说,如果程式以 MSVC 2005 建置出 Debug 组态的执行档,则此执行档需要有 msvcr80d.dll 存在才能顺利执行;如果是 Release 组态,则相依于 msvcr80.dll。但是如果你把相同的程式码拿到 MSVC 2008 上建置,产生出来的执行档则相依于 msvcr90d.dll 与 msvcr90.dll 两个不同的 DLL 档案。不同版本的 MSVC,都会有各自不同的相依 DLL 档案。

在 MSVC 的程式专案中,如何指定程式码要使用静态连结或者动态连结的 CRT 版本?其实很容易,只要在专案属性的「C/C++」页面中,选择「程式码产生」(Code Generation) 子页面,其中有个「执行阶段程式库」(Runtime Library) 的项目,也就是专案中用来设定 CRT 连结版本的地方。其中总共有四个选项,正好对应于上述静态连结与动态连结的四个不同程式库版本。

  • 多执行绪侦错 (/MTd):对应 LIBCMTD.lib
  • 多执行绪 (/MT):对应 LIBCMT.lib
  • 多执行绪侦错 DLL (/MDd):对应 MSVCRTD.lib
  • 多执行绪 DLL (/MD):对应 MSVCRT.lib

如果你没有做任何设定就开始建置程式的话,MSVC 的预设选项则会使用动态连结的版本。

C Runtime Library

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/2087387.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-07-22
下一篇 2022-07-22

发表评论

登录后才能评论

评论列表(0条)

保存