error link总结_第1页
error link总结_第2页
error link总结_第3页
error link总结_第4页
error link总结_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、already defined in *.obj“符号已定义”问题原理及解决方案C+Base 2010-06-15 15:08:25 阅读103 评论0   字号:大中小 订阅 造成LNK2005错误主要有以下几种情况: 1重复定义全局变量。可能存在两种情况: A、对于一些初学编程的程序员,有时候会以为需要使用全局变量的地方就可以使用定义申明一下。其实这是错误的,全局变量是针对整个工程的。正确的应该是在一个CPP文件中定义如下:int g_Test;那么在使用的CPP文件中就应该使用:extern int g_Test即可,如果还是使用int g_Test,那么就

2、会产生LNK2005错误,一般错误错误信息类似:AAA.obj error LNK2005 int book c?book3HA already defined in BBB.obj。切记的就是不能给变量赋值否则还是会有LNK2005错误。              这里需要的是“声明”,不是“定义”!根据C+标准的规定,一个变量是声明,必须同时满足两个条件,否则就是定义: (1)声明必须使用extern关键字;(2)不能给变量赋初值 所以,下面的是声明: extern in

3、t a; 下面的是定义 int a; int a = 0; extern int a =0; B、对于那么编程不是那么严谨的程序员,总是在需要使用变量的文件中随意定义一个全局变量,并且对于变量名也不予考虑,这也往往容易造成变量名重复,而造成LNK2005错误。 2头文件的包含重复。往往需要包含的头文件中含有变量、函数、类的定义,在其它使用的地方又不得不多次包含之,如果头文件中没有相关的宏等防止重复链接的措施,那么就会产生LNK2005错误。解决办法是在需要包含的头文件中做类似的处理:#ifndef MY_H_FILE      /如果没有定义这

4、个宏 #define MY_H_FILE      /定义这个宏 .      /头文件主体内容 . #endif 上面是使用宏来做的,也可以使用预编译来做,在头文件中加入: #pragma once /头文件主体 3使用第三方的库造成的。这种情况主要是C运行期函数库和MFC的库冲突造成的。具体的办法就是将那个提示出错的库放到另外一个库的前面。另外选择不同的C函数库,可能会引起这个错误。微软和C有两种C运行期函数库,一种是普通的函数库:LIBC.LIB,不支持多线程。另外一种是支持多线程的:ms

5、vcrt.lib。如果一个工程里,这两种函数库混合使用,可能会引起这个错误,一般情况下它需要MFC的库先于C运行期函数库被链接,因此建议使用支持多线程的msvcrt.lib。所以在使用第三方的库之前首先要知道它链接的是什么库,否则就可能造成LNK2005错误。如果不得不使用第三方的库,可以尝试按下面所说的方法修改,但不能保证一定能解决问题,前两种方法是微软提供的: A、选择VC菜单Project->Settings->Link->Catagory选择Input,再在Ignore libraries 的Edit栏中填入你需要忽略的库,如:Nafxcwd.lib;Libcmtd.

6、lib。然后在Object/library Modules的Edit栏中填入正确的库的顺序,这里需要你能确定什么是正确的顺序,呵呵,God bless you! B、选择VC菜单Project->Settings->Link页,然后在Project Options的Edit栏中输入/verbose:lib,这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。 C、选择VC菜单Project->Settings->C/C+页,Catagory选择Code Generation后再在User Runtime libraray中选择MultiThread DLL等其他库

7、,逐一尝试。 关于编译器的相关处理过程,参考: 这就是我所遇到过的LNK2005错误的几种情况,肯定还有其他的情况也可能造成这种错误,所以我不希望你在看完这篇文章以后,再遇到LNK2005错误时候,不动脑筋的想对号入座的排除错误。编程的过程就是一个思考的过程,所以还是多多开动你的头脑,那样收获会更多! 附录:编译器处理相关 一.预处理器-编译器-汇编器-链接器预处理器会处理相关的预处理指令,一般是以"#"开头的指令。如:#include "xx.h" #define等。编译器把对应的*.cpp翻译成*.s文件(汇编语言)。汇编器则处理*.s生成对应的*.

8、o文件(obj目标文件)最后链接器把所有的*.o文件链接成一个可执行文件(?.exe)1.部件:首先要知道部件(可以暂且狭义地理解为一个类)一般分为头文件(我喜欢称为接口,如:*.h)及实现文件(如:*.cpp)。一般头文件会是放一些用来作声明的东东作为接口而存在的。而实现文件主要是实现的具体代码。2.编译单个文件:记住IDE在bulid文件时只编译实现文件(如*.cpp)来产生obj,在vc下你可以对某个?.cpp按下ctrl+f7单独编译它生成对应一个?.obj文件。在编译?.cpp时IDE会在?.cpp中按顺序处理用#include包括进来的头文件(如果该头文件中又#include有文件

9、,同样会按顺序跟进处理各个头文件,如此递归。)3.内部链接与外部链接:内、外链接是比较基础的东东,但是也是新手最容易错的地方,所以这里有必要祥细讨论一下。内部链接产生的符号只在本地?.obj中可见,而外部链接的符号是所有*.obj之间可见的。如:用inline的是内部链接,在文件头中直接声明的变量、不带inline的全局函数都是外部链接。在文件头中类的内部声明的函数(不带函数体)是外部链接,而带函数体一般会是内部链接(因为IDE会尽量把它作为内联函数)认识内部链接与外部链接有什么作用呢?下面用vc6举个例子:/ 文件main.cpp内容:void main()/ 文件t1.cpp内容:#inc

10、lude "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.cpp按ctrl+f7

11、编译生成t1.obj通过打开t2.cpp按ctrl+f7编译生成t2.obj通过而当你链接时会发现:Linking.t2.obj : error LNK2005: "void _cdecl Foo(void)" (?FooYAXXZ) already defined in t1.obj这是因为:1. 编译t1.cpp在处理到#include "a.h"中的Foo时看到的Foo函数原型定义是外部链接的,所以在t1.obj中记录Foo符号是外部的。2. 编译t2.cpp在处理到#include "a.h"中的Foo时看到的Foo函数原型定

12、义是外部链接的,所以在t2.obj中记录Foo符号是外部的。3. 最后在链接 t1.obj 及 t2.obj 时, vc发现有两处地方(t1.obj和t2.obj中)定义了相同的外部符号(注意:是定义,外部符号可以多处声明但不可多处定义,因为外部符号是全局可见的,假设这时有t3.cpp声明用到了这个符号就不知道应该调用t1.obj中的还是t2.obj中的了),所以会报错。解决的办法有几种: a.将a.h中的定义改写为声明,而用另一个文件a.cpp来存放函数体。(提示:把上述程序改来试试)(函数体放在其它任何一个cpp中如t1.cpp也可以,不过良好的习惯是用对应cpp文件来存放)。这时包括a.

13、h的文件除了a.obj中有函数体代码外,其它包括a.h的cpp生成的obj文件都只有对应的符号而没有函数体,如t1.obj、t2.obj就只有符号,当最后链接时IDE会把a.obj的Foo()函数体链接进exe文件中,并把t1.obj、t2.obj中的Foo符号转换成对应在函数体exe文件中的地址。另外:当变量放在a.h中会变成全局变量的定义,如何让它变为声明呢?例如: 我们在a.h中加入:class CFoo;CFoo* obj;这时按f7进行build时出现:Linking.t2.obj : error LNK2005: "class CFoo * obj" (?obj

14、3PAVCFooA) already defined in t1.obj一个好办法就是在a.cpp中定义此变量( CFoo* obj,然后拷贝此定义到a.h文件中并在前面加上extern(extern CFoo* obj如此就可通过了。当然extern也可以在任何调用此变量的位置之前声明,不过强烈建议不要这么作,因为到处作用extern,会导致接口不统一。良好的习惯是接口一般就放到对应的头文件。b. 将a.h中的定义修改成内部链接,即加上inline关键字,这时每个t1.obj和t2.obj都存放有一份Foo函数体,但它们不是外部符号,所以不会被别的obj文件引用到,故不存在冲突。(提示:把上

15、述程序改来试试)另外我作了个实验来验证”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.cpp按ctrl+f7单独编译,并把编译后的t1.obj修改成t1.obj_inline(3) 选t2.cpp

16、按ctrl+f7单独编译,并把编译后的t2.obj修改成t2.obj_inline(4) 把除了t1.obj_inline及t2.obj_inline外的其它编译生成的文件删除。(5) 修改a.h内容为:void Foo( ) ,使之变为非内联函数作测试(6) rebuild all所有文件。这时提示:Linking.t2.obj : error LNK2005: "void _cdecl Foo(void)" (?FooYAXXZ) already defined in t1.objDebug/cle.exe : fatal error LNK1169: one or m

17、ore 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.

18、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

19、 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:y

20、es /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept Debug/t1.obj Debug/t2.obj Debug/main.objpause注意前面多了Link.exe,后面多了Debug/t1.obj Debug/t2.obj Debug/main.obj以及最后一个pause批处理命令,然后把它另存到工程目录(此目录下会看到debug目录)下起名为link.bat运行它,就会看到:t2.obj : error LNK2005: "v

21、oid _cdecl Foo(void)" (?FooYAXXZ) already defined in t1.objDebug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found很好,我们链接原来的obj文件得到的效果跟在vc中用rebuild all出来的效果一样。那么现在如果我们把备份出来的t1.obj_inline覆盖t1.obj而t2.obj_inline覆盖t2.obj再手动链接应该会是不会出错的,因为原t1.obj_inline及t2.obj_inline中存放的是内部链接符

22、号。好运行Link.bat,果然不出所料,链接成功了,看看debug目录下多出了一个exe文件。这就说明了内或外符号在obj有标志标识!(提示:上述为什么不用vc的f7build链接呢,因为文件时间改变了,build会重新生成新的obj,所以我们用手动链接保证obj不变)注bj信息可用dumpbin.exe查看4.#include规则:有很多人不知道#include 文件该放在何处?1). 增强部件自身的完整性:为了保证部件完整,部件的cpp实现文件(如test.cpp)中第一个#include的应当是它自身对应的头文件(如test.h)。(除非你用预编译头文件, 预编译头必须放在第一个)。这

23、样就保证了该部件头文件(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 CFoovoid Foo();/ 文件test.h:#include "foo.h"class CTestCFoo* m_pFoo;public:CTest() : m_pFoo(NULL)void Test() if(m_pFoo) m_pFoo->Foo();.;/ 文件test.cpp:#include "test.h".如上文件test.h中我们其实可以#include

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论