`

already defined in *.obj(转载)

 
阅读更多

造成LNK2005错误主要有以下几种情况:
1
.重复定义全局变量。可能存在两种情况:
 
A
、对于一些初学编程的程序员,有时候会以为需要使用全局变量的地方就可以使用定义申明一下。其实这是错误的,全局变量是针对整个工程的。正确的应该是在一个CPP文件中定义如下:int g_Test;那么在使用的CPP文件中就应该使用:extern int g_Test即可,如果还是使用int g_Test,那么就会产生LNK2005错误,一般错误错误信息类似:AAA.obj error LNK2005 int book c
book@@3HA already defined in BBB.obj。切记的就是不能给变量赋值否则还是会有LNK2005错误。 
             
这里需要的是声明,不是定义!根据C++标准的规定,一个变量是声明,必须同时满足两个条件,否则就是定义:
 
(1)
声明必须使用extern关键字;(2)不能给变量赋初值
 
所以,下面的是声明
: 
extern int a; 
下面的是定义
 
int a; int a = 0; extern int a =0; 
B
、对于那么编程不是那么严谨的程序员,总是在需要使用变量的文件中随意定义一个全局变量,并且对于变量名也不予考虑,这也往往容易造成变量名重复,而造成LNK2005错误。
 

2
.头文件的包含重复。往往需要包含的头文件中含有变量、函数、类的定义,在其它使用的地方又不得不多次包含之,如果头文件中没有相关的宏等防止重复链接的措施,那么就会产生LNK2005错误。解决办法是在需要包含的头文件中做类似的处理:#ifndef MY_H_FILE      //如果没有定义这个宏
 
#define MY_H_FILE      //
定义这个宏
 
…….      //
头文件主体内容
 
……. 
#endif 
上面是使用宏来做的,也可以使用预编译来做,在头文件中加入:
 
#pragma once 
//
头文件主体
 
3
.使用第三方的库造成的。这种情况主要是C运行期函数库和MFC的库冲突造成的。具体的办法就是将那个提示出错的库放到另外一个库的前面。另外选择不同的C函数库,可能会引起这个错误。微软和C有两种C运行期函数库,一种是普通的函数库:LIBC.LIB,不支持多线程。另外一种是支持多线程的:msvcrt.lib。如果一个工程里,这两种函数库混合使用,可能会引起这个错误,一般情况下它需要MFC的库先于C运行期函数库被链接,因此建议使用支持多线程的msvcrt.lib。所以在使用第三方的库之前首先要知道它链接的是什么库,否则就可能造成LNK2005错误。如果不得不使用第三方的库,可以尝试按下面所说的方法修改,但不能保证一定能解决问题,前两种方法是微软提供的:
 
A
、选择VC菜单Project->Settings->Link->Catagory选择Input,再在Ignore libraries Edit栏中填入你需要忽略的库,如:Nafxcwd.lib;Libcmtd.lib。然后在Object/library ModulesEdit栏中填入正确的库的顺序,这里需要你能确定什么是正确的顺序,呵呵,God bless you
 
B
、选择VC菜单Project->Settings->Link页,然后在Project OptionsEdit栏中输入/verbose:lib,这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。
 
C
、选择VC菜单Project->Settings->C/C++页,Catagory选择Code Generation后再在User Runtime libraray中选择MultiThread DLL等其他库,逐一尝试。
 
关于编译器的相关处理过程,参考:
 
http://www.donews.net/xzwenlan/archive/2004/12/23/211668.aspx 

这就是我所遇到过的LNK2005错误的几种情况,肯定还有其他的情况也可能造成这种错误,所以我不希望你在看完这篇文章以后,再遇到LNK2005错误时候,不动脑筋的想对号入座的排除错误。编程的过程就是一个思考的过程,所以还是多多开动你的头脑,那样收获会更多!

附录:

编译器处理相关 
.预处理器-编译器-汇编器-链接器

预处理器会处理相关的预处理指令,一般是以"#"开头的指令。如:#include "xx.h" #define等。
编译器把对应的*.cpp翻译成*.s文件(汇编语言)
汇编器则处理*.s生成对应的*.o文件(obj目标文件)
最后链接器把所有的*.o文件链接成一个可执行文件(?.exe)

1.部件:
首先要知道部件(可以暂且狭义地理解为一个类)一般分为头文件(我喜欢称为接口,如:*.h)及实现文件(:*.cpp)

一般头文件会是放一些用来作声明的东东作为接口而存在的。
而实现文件主要是实现的具体代码。

2.编译单个文件:
记住IDEbulid文件时只编译实现文件(*.cpp)来产生obj,在vc下你可以对某个?.cpp按下ctrl+f7单独编译它

生成对应一个?.obj文件。在编译?.cppIDE会在?.cpp中按顺序处理用#include包括进来的头文件
(
如果该头文件中又#include有文件,同样会按顺序跟进处理各个头文件,如此递归。。)

3.内部链接与外部链接:
内、外链接是比较基础的东东,但是也是新手最容易错的地方,所以这里有必要祥细讨论一下。

内部链接产生的符号只在本地?.obj中可见,而外部链接的符号是所有*.obj之间可见的。
:inline的是内部链接,在文件头中直接声明的变量、不带inline的全局函数都是外部链接。
在文件头中类的内部声明的函数(不带函数体)是外部链接,而带函数体一般会是内部链接(因为IDE会尽量把它作为内联函数)
认识内部链接与外部链接有什么作用呢?下面用vc6举个例子:

// 
文件main.cpp内容:
void main(){}
// 
文件t1.cpp内容
:
#include "a.h"
void Test1(){ Foo(); }
// 
文件t2.cpp内容
:
#include "a.h"
void Test2(){ Foo(); }
// 
文件a.h内容
:
void Foo( ){ }
好,用vc生成一个空的console程序(File - new - projects - win32 console application),并关掉预编译选项开关

(project - setting - Cagegoryrecompiled Headers - Not using precompiled headers)
现在你打开t1.cppctrl+f7编译生成t1.obj通过
打开t2.cppctrl+f7编译生成t2.obj通过
而当你链接时会发现:
Linking...
t2.obj : error LNK2005: "void __cdecl Foo(void)" (
?Foo@@YAXXZ) already defined in t1.obj
这是因为:
1. 
编译t1.cpp在处理到#include "a.h"中的Foo时看到的Foo函数原型定义是外部链接的,所以在t1.obj中记录Foo符号是外部的。

2. 
编译t2.cpp在处理到#include "a.h"中的Foo时看到的Foo函数原型定义是外部链接的,所以在t2.obj中记录Foo符号是外部的。
3. 
最后在链接t1.obj t2.obj , vc发现有两处地方(t1.objt2.obj)定义了相同的外部符号(注意:是定义,外部符号可以
多处声明但不可多处定义,因为外部符号是全局可见的,假设这时有t3.cpp声明用到了这个符号就不知道应该调用t1.obj
中的还是t2.obj中的了),所以会报错。

解决的办法有几种: 
a.
a.h中的定义改写为声明,而用另一个文件a.cpp来存放函数体。(提示:把上述程序改来试试
)
(
函数体放在其它任何一个cpp中如t1.cpp也可以,不过良好的习惯是用对应cpp文件来存放)

这时包括a.h的文件除了a.obj中有函数体代码外,
其它包括a.hcpp生成的obj文件都只有对应的符号而没有函数体,如t1.objt2.obj就只有符号,当最后链接时IDE会把
a.obj
Foo()函数体链接进exe文件中,并把t1.objt2.obj中的Foo符号转换成对应在函数体exe文件中的地址。
另外:当变量放在a.h中会变成全局变量的定义,如何让它变为声明呢?
例如: 我们在a.h中加入
:class CFoo{};CFoo* obj;
这时按f7进行build时出现
:
Linking...
t2.obj : error LNK2005: "class CFoo * obj" (
?obj@@3PAVCFoo@@A) already defined in t1.obj
一个好办法就是在a.cpp中定义此变量( CFoo* obj,然后拷贝此定义到a.h文件中并在前面加上extern(extern CFoo* obj
如此就可通过了。当然extern也可以在任何调用此变量的位置之前声明,不过强烈建议不要这么作,因为到处作用extern,会

导致接口不统一。良好的习惯是接口一般就放到对应的头文件。

b. a.h中的定义修改成内部链接,即加上inline关键字,这时每个t1.objt2.obj都存放有一份Foo函数体,但它们不是外部
符号,所以不会被别的obj文件引用到,故不存在冲突。(提示:把上述程序改来试试)
另外我作了个实验来验证”vc是把是否是外部符号的标志记录在obj文件中的“(有点绕口)。可以看看,如下
:
(1)
文件内容
:
// 
文件main.cpp内容
:
void main(){}
// 
文件t1.cpp内容
:
#include "a.h"
void Test1(){ Foo(); }
// 
文件t2.cpp内容
:
#include "a.h"
void Test2(){ Foo(); }
// 
文件a.h内容
:
inline void Foo( ){ }
(2) 
t1.cppctrl+f7单独编译,并把编译后的t1.obj修改成
t1.obj_inline
(3) 
t2.cppctrl+f7单独编译,并把编译后的t2.obj修改成
t2.obj_inline
(4) 
把除了t1.obj_inlinet2.obj_inline外的其它编译生成的文件删除。

(5) 
修改a.h内容为:void Foo( ){ },使之变为非内联函数作测试
(6) rebuild all
所有文件。这时提示:
Linking...
t2.obj : error LNK2005: "void __cdecl Foo(void)" (
?Foo@@YAXXZ) already defined in t1.obj
Debug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found
(7) 
好,看看工程目录下的debug目录中会看到新生成的obj文件。
下面我们来手工链接看看,
打开菜单中的project - setting - Link,拷贝Project options下的所有内容,如下:
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept 
把它修改成
:
Link.exe kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept Debug/t1.obj Debug/t2.obj Debug/main.obj
pause
注意前面多了Link.exe,后面多了Debug/t1.obj Debug/t2.obj Debug/main.obj以及

最后一个pause批处理命令,然后把它另存到工程目录(此目录下会看到debug目录)下起名为link.bat
运行它,就会看到
:
t2.obj : error LNK2005: "void __cdecl Foo(void)" (
?Foo@@YAXXZ) already defined i
n t1.obj
Debug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found
很好,我们链接原来的obj文件得到的效果跟在vc中用rebuild all出来的效果一样。那么现在如果
我们把备份出来的t1.obj_inline覆盖t1.objt2.obj_inline覆盖t2.obj再手动链接应该会是
不会出错的,因为原t1.obj_inlinet2.obj_inline中存放的是内部链接符号。好运行Link.bat,果然
不出所料,链接成功了,看看debug目录下多出了一个exe文件。这就说明了内或外符号在obj有标志标识!
(
提示:上述为什么不用vcf7build链接呢,因为文件时间改变了,build会重新生成新的obj

所以我们用手动链接保证obj不变)[bj信息可用dumpbin.exe查看]


4.#include
规则:
有很多人不知道#include 文件该放在何处?

1). 增强部件自身的完整性:
为了保证部件完整,部件的cpp实现文件(test.cpp)中第一个#include的应当是它自身对应的头文件(test.h)
(
除非你用预编译头文件, 预编译头必须放在第一个)。这样就保证了该部件头文件(test.h)所必须依赖的其它接口(a.h)要放到它对应的文件头中(test.h),而不是在cpp(test.cpp)把所依赖的其它头文件(a.h)移到其自身对应的头文件(test.h)之前(因为这样强迫其它包括此部件的头文件(test.h)的文件(b.cpp)也必须再写一遍include(b.cpp若要#include "test.h"也必须#include "a.h")”。另外我们一般会尽量减少文件头之间的依赖关系,看下面:

2). 减少部件之间的依赖性:
1的基础上尽量把#include到的文件放在cpp中包括。
这就要求我们一般不要在头文件中直接引用其它变量的实现,而是把此引用搬到实现文件中。
例如: 
// 
文件
foo.h:
class CFoo{
void Foo(){}
};
// 
文件
test.h:
#include "foo.h"
class CTest{
CFoo* m_pFoo;
public:
CTest() : m_pFoo(NULL){}
void Test(){ if(m_pFoo){ m_pFoo->Foo();}}
...........
};
// 
文件
test.cpp:
#include "test.h"
.....

如上文件test.h中我们其实可以#include "foo.h"移到test.cpp文件中。因为CFoo* m_pFoo我们只想在部件CTest中用到,
而将来想用到CTest部件而包括test.h的其它部件没有必要见到foo.h接口,所以我们用前向声明修改原文件如下
:
// 
文件
foo.h:
class CFoo{
public:
void Foo(){}
};
// 
文件
test.h:
class CFoo;
class CTest{
CFoo* m_pFoo;
public:
CTest();
void Test();
//........
};
// 
文件
test.cpp:
#include "test.h" // 
这里第一个放该部件自身对应的接口头文件

#include "foo.h" // 
该部件用到了foo.h
CTest::CTest() : m_pFoo(0){ 
m_pFoo = new CFoo; 
}
void CTest::Test(){ 
if(m_pFoo){ 
m_pFoo->Foo();
}
}
//.....
// 
再加上main.cpp来测试
:
#include "test.h" // 
这里我们就不用见到#include "foo.h"

CTest test;
void main(){
test.Test();
}

3). 双重包含卫哨:
在文件头中包括其它头文件时(如:#include "xx.h")建议也加上包含卫哨
:
// test.h
文件内容
:
#ifndef __XX1_H_
#include "xx1.h"
#endif
#ifndef __XX2_H_
#include "xx2.h"
#endif
......

虽然我们已经在xx.h文件中开头已经加过,但是因为编译器在打开#include文件也
是需要时间的,如果在外部加上包含卫哨,对于很大的工程可以节省更多的编译时间。

分享到:
评论

相关推荐

    C++经典错误之already defined in *****.obj解决方法

    C++经典错误之already defined in *.obj解决方法 C++经典错误之already defined in *.obj解决方法是指在C++编程中遇到的一个常见错误,即链接器报告已经定义了某个符号(already defined),从而导致链接失败。这种...

    error LNK2005: DllMain already defined in corelibc.lib(dllmain.obj)

    error LNK2005: DllMain already defined in corelibc.lib(dllmain.obj)的解决方案

    uafxcwd.lib LIBCMTD.lib 弱外部链接

    2@YAPAXI@Z) already defined in LIBCMTD.lib(new.obj) uafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj) uafxcwd....

    detours2.1 VC6中编译方法及源代码及使用例子及编译好的BIN

    4.打开项目中detoured.cpp把里面DllMain函数名修改为LengFeng(否则使用时会出现DllMain冲突)error LNK2005: _DllMain@12 already defined in ***.obj 5.直接编译就可以生成detours.lib 6.在需要的项目中使用...

    HTK-3.3-alpha1.tar.gz_HTK_HTK工具箱_htk-3.3_隐马尔科夫模型_马尔科夫

    《HTK-3.3-alpha1.tar.gz:探索与应用隐马尔科夫模型的工具箱》 在信息技术领域,HTK(HMM Toolkit)是一个广泛使用的开源工具箱,专门用于构建和分析隐马尔科夫模型(Hidden Markov Models,简称HMMs)。...

    使用CxImage实现图像格式转换.pdf

    LIBCMT.lib(crt0dat.obj) : error LNK2005: _exit already defined in MSVCRTD.lib(MSVCR100D.dll) LIBCMT.lib(crt0dat.obj) : error LNK2005: __amsg_exit already defined in MSVCRTD.lib(MSVCR100D.dll) LIBCMT....

    alreadydefinedinobj“符号已定义”问题原理及解决方案.doc

    标题中的问题"already defined in *.obj“符号已定义”"通常在Visual C++环境中出现,特别是当试图在预编译头文件(stdafx.h)中定义全局变量时。由于预编译头文件在多个源文件中被包含,导致相同的全局变量被多次定义...

    UAFXCWD.LIB和UAFXCWD.PDB库文件.rar

    2@YAPAXI@Z) already defined in LIBCMTD.lib(new.obj) nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj) Debug/DCap...

    mfc与其他库冲突问题.doc

    mfcs40d.lib(dllmodul.obj):error LNK2005: _DllMain@12 already defined in MSVCRTD.LIB (dllmain.obj) mfcs42d.lib(dllmodul.obj):error LNK2005: _DllMain@12 already defined in msvcrtd.lib(dllmain.obj) ``` ...

    VC6.0常见链接错误与解决方法

    (2)error LNK2005: _main already defined in xxxx.obj 编号:LNK2005 直译:_main已经存在于xxxx.obj中了。 错误分析: 直接的原因是该程序中有多个(不止一个)main函数。这是初学C++的低年级同学在初次编程时经常...

    转载:已编译好的openssh库

    msvcrt.lib(MSVCRT.dll) : error LNK2005: _strchr already defined in libcmtd.lib(strchr.obj) ... 这是由于OpenSSL的静态函数库使用的是了VC的多线程DLL的Release版本,而我的程序使用了多线程静态链接的...

    汇编上机指导与示例huibian

    1. **Register already defined**(寄存器已定义):检查重复定义的寄存器。 2. **Unknown symbol type**(未知符号类型):检查是否使用了未定义的符号或错误的符号类型。 3. **Symbol is multi-defined**(符号...

    Keil C 编译器常见警告与错误信息的解决方法

    #if defined(USE_DELAY) void DelayX1ms(void) { // 函数体 } #endif ``` #### 6. ***WARNING 6: XDATA SPACE MEMORY OVERLAP*** **问题描述:** 此警告表示在XDATA存储空间(通常是外部数据存储器)中有...

    keilc编译器错误与解决方法.pdf

    - 使用条件编译来控制是否编译该函数,例如通过`#if defined(DEBUG) ... #endif`来仅在调试模式下编译。 #### 6. **WARNING 6 :XDATA SPACE MEMORY OVERLAP FROM : 0025H TO: 0025H** - **描述**:外部数据存储...

    C++ 编译环境中解决LINK2005问题文件

    LNK2005: symbol "symbol_name" already defined in ... ``` 这里 `symbol_name` 是一个特定的全局变量或函数名。 #### 二、错误原因分析 ##### 1. 源文件与汇编过程 当编译器处理源文件时,会经历以下两个主要...

    C++编译链接过程详解

    2. 在编译单元生成之后,便是将编译单元进行编译,生成目标文件(*.obj文件) 对于主流的编译过程,通常存在两个阶段:首先是生成汇编语言,然后使用汇编器生成机器语言。 机器语言顾名思义就是0101这样的二进制代码...

    VC6.0错误指令及解决方法

    error LNK2005: '_main' already defined in a.obj 链接器检测到`main`函数在多个对象文件中被定义。 **解决方法:** - 检查所有源文件,确保`main`函数仅在一个文件中定义。 - 如果使用了模板或库,确认没有意外...

Global site tag (gtag.js) - Google Analytics