关于形如--error LNK2005: xxx 已经在 msvcrtd.lib ( MSVCR90D.dll ) 中定义--的问题分析解决_error lnk2005: __lock 已经在 msvcrtd.lib(msvcr120d.dl-程序员宅基地

http://www.cnblogs.com/qinfengxiaoyue/archive/2013/02/01/2889668.html--原文
1.问题引出

很久没有写程序设计入门知识的相关文章了,这篇文章要来谈谈程序库 (Library) 链接,以及关于 MSVC 与 CRT 之间的种种

恩怨情仇。

如果你使用的操作系统是 Linux、Mac 或其他非 Windows 平台,你可以忽略这篇文章;如果你使用的操作系统是

Windows 平台,但没有用 Microsoft Visual Studio C++(以下简称为 MSVC)软件撰写 C++ 程序的话,这篇文章对你的

帮助可能很有限;但如果你的操作系统是 Windows,而且你使用的程序整合开发环境是 MSVC 软件撰写 C++ 程序的话,这

篇文章应该能够帮助你釐清一些重要的基础观念。

身为程序设计者,在学习程序设计的过程中,你是否曾经遇过某些看起来不知所云的错误信息,却不知该如何解决?例如当你

快快乐乐地写完程序,并且确认所有的程序代码都能成功通过编译之后,接着执行「生成解决方案」(Build Solution) 的步

骤,结果却跑出一堆莫名其妙的错误:

1>libcmtd.lib(errmode.obj) : error LNK2005: ___set_app_type 已经在msvcrtd.lib(MSVCR90D.dll) 中定义

1>libcmtd.lib(dbgrptw.obj) : error LNK2005: __CrtDbgReport已经在msvcrtd.lib(MSVCR90D.dll) 中定义

1>msvcrtd.lib(MSVCR90D.dll) : error LNK2005: __setmbcp 已经在libcmtd.lib(mbctype.obj) 中定义

1>LINK:warning LNK4098:默认库“msvcrtd.lib”与其他库的使用冲突;请使用/NODEFAULTLIB:library

1>msvcrtd.lib(cinitexe.obj) : warning LNK4098: 默认库“libcmtd.lib”与其他库的使用冲突;请使

用/NODEFAULTLIB:library

.....................

以一般的情况来说,如果在你的程序项目中有使用某些由他人所撰写的第三方程序库或是开源项目的程序库,比较容易会发生

上述的错误状况。从上述这些看似离奇而令人摸不着头线程的错误信息中,我们大概可以猜测问题点应该在于 LIBCMTD.lib

与 MSVCRTD.lib 这两个程序库身上。但到底什么是 LIBCMTD.lib 和 MSVCRTD.lib?在我们的程序代码中有使用这些程序

库吗?

答是肯定的。

2.C Runtime Library(C运行时库)

熟悉 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 并非只有单一一种版本存在。事实上,除了可以依「Debug」与「Release」用途分成两个版本之外,两者又可分别

衍生分出「静态链接」与「动态链接」两种形式:

静态链接

LIBCMTD.lib(Debug版本)

LIBCMT.lib

动态链接

MSVCRTD.lib(Debug版本)

MSVCRT.lib

虽然这四个 CRT 版本的用途与使用方式各不相同,但却有个共通的特点,就是它们都是满足执行线程安全需求,可在多执行线

程程序代码中安全使用的程序库版本。事实上,在过去 MSVC 6 的版本中,本来还有另外两个 LIBCD.lib(Debug版本)与

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 的预设选项则会使用动态链接的版本。

 

3.Standard C++ Library(标准C++库)

请注意,以上只是单纯 C 语言的程序库而没有包含 C++ 语言在内。如果你的程序系统中,有包含 C++ 语言的程序代码的

话,那又是另外一回事了。但是在项目属性的页面中,为什么找不到相关的设定选项呢?因为 MSVC 悄悄地帮程序设计者代劳

处理掉了。只要在程序代码中使用 #include 语法纳入任何一个 C++ 的标头文件,例如 iostream 或 fstream,MSVC 就

会在链接器的运作阶段中,自动帮我们链接 C++ 的执行阶段程序库。而 C++ 的执行阶段程序库,同样可分为四个版本:

静态链接

LIBCPMTD.lib(Debug版本)

LIBCPMT.lib

动态链接:

MSVCPRTD.lib(Debug版本):执行文件相依于 MSVCP90D.dll

MSVCPRT.lib:执行文件相依于 MSVCP90.dll

至于程序执行文件使用的是静态链接或者动态链接的版本,就仰赖于 C 语言的版本设定选项了。举个例子来说,如果你撰写了

一个 Debug 组态的 C++ 程序,并且保留项目原先预设的生成选项(动态链接),那么最终生成出来的程序执行文件将会相

依于 MSVCR90D.dll 以及 MSVCP90D.dll 两个 DLL 文件。如果将相同的程序以 Release 组态生成完成,则会相依于

MSVCR90.dll 以及 MSVCP90.dll 二者。

 

4.问题分析及解决

刚学习程序设计的入门者,经常会在满心欢喜地完成一件程序作品并且传给其他人使用时,却发现不能在别人的电脑上启动程

序,其实就是陷入了使用者电脑缺少 DLL文件而无法执行程序的窘境。有三种方法可以解决这个令人困扰的问题:

1 . 使用者的电脑,必须先安装「Visual C++开发套件」(MSVC 2008 或 MSVC 2005 )。

2 . 将所需的 DLL文件,例如 MSVCR90D.dll与MSVCP90.dll,直接附在程序的下载包当中。

3 . 以静态链接方式生成程序执行文件。

当你无法确定自己的程序或别人的程序,是否相依于某些特定的 DLL文件时,有一个非常好用的免费工具程序 Dependency

Walker,可以开启 EXE格式的执行文件或者 DLL格式的动态程序库,然后详细地条列出它们所相依的 DLL文件。

了解了几种不同的 CRT版本选项之后,回到最前面的错误信息问题,相信各位现在应该能够很清楚地理解,原来会发生这些奇

怪的错误状况,是因为程序同时链接了 LIBCMTD.lib与MSVCRTD.lib而造成函数定义版本冲突也就是说,程序链接器已经

在其中一个 CRT的版本中找到所需的函数定义,但此时却又跳出另外一位 CRT,也给了一份相同函数的实现版本,所以链接

器无法判断应该忽略谁并且选择谁。

而这个状况的发生原因,就是你的程序与程序所链接的外部程序库,使用了不同的 CRT版本之故。例如,当你的程序使用了

Lua,自然必须链接至 Lua的程序库 lua5.1.lib,但如果lua5.1.lib是以静态链接版本的 CRT生成而成,而你的程序却是以预

设选项,动态链接 CRT 来生成程序执行文件的话,如此一来就会产生上述这些错误信息了。至此,问题的答已昭然若揭,

方法有二种

其一是将Lua重新以动态链接 CRT 的方式生成出一个新的程序库;

其二则是将自己的程序项目改成以静态链接 CRT 方式生成。

换个角度想,当你身为一位程序库的设计开发者,想要将自己写的东西分享给其他人,但又不想要完全开放自己撰写的程序源

码时,至少可以同时提供以下四种版本的程序库,以妥善满足使用者的各种不同需求:

Debug:动态链接Debug版本

Release:动态链接版本

Debug_Static:静态链接Debug版本

Release_Static:静态链接版本

然而,有时候世界并不会运作得如此理想。在某些特殊的状况下,当我们使用他人所写的第三方程序库时,有时可能只拿得到

其中某个特定的版本,例如 Release_Static版本时,就很有可能会遇到程序库冲突的错误情形。此时就需要视项目的实际需求

而定,可以在项目属性中指定「忽略特定程序库」(Ignore Specific Library)这个选项,让程序代码链接器忽略某些程序库,

以此化解动静程序库或新旧程序库之间的恩怨冲突。

小测验:你所撰写的程序,必须链接某个以静态多执行线程 (/MT) CRT 生成而成的程序库。如果你的程序在 Debug组态下以

多执行线程侦错 (/MTd)选项生成,是否会产生冲突?如果你的程序在 Release组态下以多执行线程 (/MT) 选项生成,是否

会产生冲突?是的话,应该如何解决?

上面的方法还是不行!会出现其他问题的。

以下是我摸索出的最新的解决方法:

首先,所有的lib文件,使用/MTd或/MT编译(注:即静态链接模式)。Debug调试模式使用/MTd,Release模式使用/MT。

然后,在自己的程序中也使用/MTd或/MT编译。这样就不会出问题了。

三种编译链接库的方式:

(1)连接Windows库。针对Win32 API编写的应用程序,上面的方法可能带来新问题,可以忽略libcmt.lib库,即可。如果还

有其他问题,再忽略相应的库。

(2)MFC静态链接。上面的方法就是针对这种链接方式的,所以没问题。

(3)MFC动态链接。没有试过,应该和(1)类似。

最后补充:如果还不行,直接加入/force:multiple编译参数吧。这次之所以没有使用它,也是为了严谨起见。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xtsokxtsok/article/details/25871253

智能推荐

C#连接OPC C#上位机链接PLC程序源码 1.该程序是通讯方式是CSharp通过OPC方式连接PLC_c#opc通信-程序员宅基地

文章浏览阅读565次。本文主要介绍如何使用C#通过OPC方式连接PLC,并提供了相应的程序和学习资料,以便读者学习和使用。OPC服务器是一种软件,可以将PLC的数据转换为标准的OPC格式,允许其他软件通过标准接口读取或控制PLC的数据。此外,本文还提供了一些学习资料,包括OPC和PLC的基础知识,C#编程语言的教程和实例代码。这些资料可以帮助读者更好地理解和应用本文介绍的程序。1.该程序是通讯方式是CSharp通过OPC方式连接PLC,用这种方式连PLC不用考虑什么种类PLC,只要OPC服务器里有的PLC都可以连。_c#opc通信

Hyper-V内的虚拟机复制粘贴_win10 hyper-v ubuntu18.04 文件拷贝-程序员宅基地

文章浏览阅读1.6w次,点赞3次,收藏10次。实践环境物理机:Windows10教育版,操作系统版本 17763.914虚拟机:Ubuntu18.04.3桌面版在Hyper-V中的刚安装好Ubuntu虚拟机之后,会发现鼠标滑动很不顺畅,也不能向虚拟机中拖拽文件或者复制内容。在VMware中,可以通过安装VMware tools来使物理机和虚拟机之间达到更好的交互。在Hyper-V中,也有这样的工具。这款工具可以完成更好的鼠标交互,我的..._win10 hyper-v ubuntu18.04 文件拷贝

java静态变量初始化多线程,持续更新中_类初始化一个静态属性 为线程池-程序员宅基地

文章浏览阅读156次。前言互联网时代,瞬息万变。一个小小的走错,就有可能落后于别人。我们没办法去预测任何行业、任何职业未来十年会怎么样,因为未来谁都不能确定。只能说只要有互联网存在,程序员依然是个高薪热门行业。只要跟随着时代的脚步,学习新的知识。程序员是不可能会消失的,或者说不可能会没钱赚的。我们经常可以听到很多人说,程序员是一个吃青春饭的行当。因为大多数人认为这是一个需要高强度脑力劳动的工种,而30岁、40岁,甚至50岁的程序员身体机能逐渐弱化,家庭琐事缠身,已经不能再进行这样高强度的工作了。那么,这样的说法是对的么?_类初始化一个静态属性 为线程池

idea 配置maven,其实不用单独下载Maven的。以及设置新项目配置,省略每次创建新项目都要配置一次Maven_安装idea后是不是不需要安装maven了?-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏43次。说来也是惭愧,一直以来,在装环境的时候都会从官网下载Maven。然后再在idea里配置Maven。以为从官网下载的Maven是必须的步骤,直到今天才得知,idea有捆绑的 Maven 我们只需要搞一个配置文件就行了无需再官网下载Maven包以后再在新电脑装环境的时候,只需要下载idea ,网上找一个Maven的配置文件 放到 默认的 包下面就可以了!也省得每次创建项目都要重新配一次Maven了。如果不想每次新建项目都要重新配置Maven,一种方法就是使用默认的配置,另一种方法就是配置 .._安装idea后是不是不需要安装maven了?

奶爸奶妈必看给宝宝摄影大全-程序员宅基地

文章浏览阅读45次。家是我们一生中最重要的地方,小时候,我们在这里哭、在这里笑、在这里学习走路,在这里有我们最真实的时光,用相机把它记下吧。  很多家庭在拍摄孩子时有一个看法,认为儿童摄影团购必须是在风景秀丽的户外,即便是室内那也是像大酒店一样...

构建Docker镜像指南,含实战案例_rocker/r-base镜像-程序员宅基地

文章浏览阅读429次。Dockerfile介绍Dockerfile是构建镜像的指令文件,由一组指令组成,文件中每条指令对应linux中一条命令,在执行构建Docker镜像时,将读取Dockerfile中的指令,根据指令来操作生成指定Docker镜像。Dockerfile结构:主要由基础镜像信息、维护者信息、镜像操作指令、容器启动时执行指令。每行支持一条指令,每条指令可以携带多个参数。注释可以使用#开头。指令说明FROM 镜像 : 指定新的镜像所基于的镜像MAINTAINER 名字 : 说明新镜像的维护(制作)人,留下_rocker/r-base镜像

随便推点

毕设基于微信小程序的小区管理系统的设计ssm毕业设计_ssm基于微信小程序的公寓生活管理系统-程序员宅基地

文章浏览阅读223次。该系统将提供便捷的信息发布、物业报修、社区互动等功能,为小区居民提供更加便利、高效的服务。引言: 随着城市化进程的加速,小区管理成为一个日益重要的任务。因此,设计一个基于微信小程序的小区管理系统成为了一项具有挑战性和重要性的毕设课题。本文将介绍该小区管理系统的设计思路和功能,以期为小区提供更便捷、高效的管理手段。四、总结与展望: 通过本次毕设项目,我们实现了一个基于微信小程序的小区管理系统,为小区居民提供了更加便捷、高效的服务。通过该系统的设计与实现,能够提高小区管理水平,提供更好的居住环境和服务。_ssm基于微信小程序的公寓生活管理系统

如何正确的使用Ubuntu以及安装常用的渗透工具集.-程序员宅基地

文章浏览阅读635次。文章来源i春秋入坑Ubuntu半年多了记得一开始学的时候基本一星期重装三四次=-= 尴尬了 觉得自己差不多可以的时候 就吧Windows10干掉了 c盘装Ubuntu 专心学习. 这里主要来说一下使用Ubuntu的正确姿势Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的开源GNU/Linux操作系统,Ubuntu 是基于DebianGNU/Linux,支..._ubuntu安装攻击工具包

JNI参数传递引用_jni引用byte[]-程序员宅基地

文章浏览阅读335次。需求:C++中将BYTE型数组传递给Java中,考虑到内存释放问题,未采用通过返回值进行数据传递。public class demoClass{public native boolean getData(byte[] tempData);}JNIEXPORT jboolean JNICALL Java_com_core_getData(JNIEnv *env, jobject thisObj, jbyteArray tempData){ //resultsize为s..._jni引用byte[]

三维重建工具——pclpy教程之点云分割_pclpy.pcl.pointcloud.pointxyzi转为numpy-程序员宅基地

文章浏览阅读2.1k次,点赞5次,收藏30次。本教程代码开源:GitHub 欢迎star文章目录一、平面模型分割1. 代码2. 说明3. 运行二、圆柱模型分割1. 代码2. 说明3. 运行三、欧几里得聚类提取1. 代码2. 说明3. 运行四、区域生长分割1. 代码2. 说明3. 运行五、基于最小切割的分割1. 代码2. 说明3. 运行六、使用 ProgressiveMorphologicalFilter 分割地面1. 代码2. 说明3. 运行一、平面模型分割在本教程中,我们将学习如何对一组点进行简单的平面分割,即找到支持平面模型的点云中的所有._pclpy.pcl.pointcloud.pointxyzi转为numpy

以NFS启动方式构建arm-linux仿真运行环境-程序员宅基地

文章浏览阅读141次。一 其实在 skyeye 上移植 arm-linux 并非难事,网上也有不少资料, 只是大都遗漏细节, 以致细微之处卡壳,所以本文力求详实清析, 希望能对大家有点用处。本文旨在将 arm-linux 在 skyeye 上搭建起来,并在 arm-linux 上能成功 mount NFS 为目标, 最终我们能在 arm-linux 里运行我们自己的应用程序. 二 安装 Sky..._nfs启动 arm

攻防世界 Pwn 进阶 第二页_pwn snprintf-程序员宅基地

文章浏览阅读598次,点赞2次,收藏5次。00为了形成一个体系,想将前面学过的一些东西都拉来放在一起总结总结,方便学习,方便记忆。攻防世界 Pwn 新手攻防世界 Pwn 进阶 第一页01 4-ReeHY-main-100超详细的wp1超详细的wp203 format2栈迁移的两种作用之一:栈溢出太小,进行栈迁移从而能够写入更多shellcode,进行更多操作。栈迁移一篇搞定有个陌生的函数。C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 _pwn snprintf

推荐文章

热门文章

相关标签