error link总结Word下载.docx

上传人:b****6 文档编号:17347033 上传时间:2022-12-01 格式:DOCX 页数:7 大小:20.74KB
下载 相关 举报
error link总结Word下载.docx_第1页
第1页 / 共7页
error link总结Word下载.docx_第2页
第2页 / 共7页
error link总结Word下载.docx_第3页
第3页 / 共7页
error link总结Word下载.docx_第4页
第4页 / 共7页
error link总结Word下载.docx_第5页
第5页 / 共7页
点击查看更多>>
下载资源
资源描述

error link总结Word下载.docx

《error link总结Word下载.docx》由会员分享,可在线阅读,更多相关《error link总结Word下载.docx(7页珍藏版)》请在冰豆网上搜索。

error link总结Word下载.docx

下面的是定义

inta;

inta=0;

externinta=0;

B、对于那么编程不是那么严谨的程序员,总是在需要使用变量的文件中随意定义一个全局变量,并且对于变量名也不予考虑,这也往往容易造成变量名重复,而造成LNK2005错误。

2.头文件的包含重复。

往往需要包含的头文件中含有变量、函数、类的定义,在其它使用的地方又不得不多次包含之,如果头文件中没有相关的宏等防止重复链接的措施,那么就会产生LNK2005错误。

解决办法是在需要包含的头文件中做类似的处理:

#ifndefMY_H_FILE 

//如果没有定义这个宏

#defineMY_H_FILE 

//定义这个宏

……. 

//头文件主体内容

…….

#endif

上面是使用宏来做的,也可以使用预编译来做,在头文件中加入:

#pragmaonce

//头文件主体

3.使用第三方的库造成的。

这种情况主要是C运行期函数库和MFC的库冲突造成的。

具体的办法就是将那个提示出错的库放到另外一个库的前面。

另外选择不同的C函数库,可能会引起这个错误。

微软和C有两种C运行期函数库,一种是普通的函数库:

LIBC.LIB,不支持多线程。

另外一种是支持多线程的:

msvcrt.lib。

如果一个工程里,这两种函数库混合使用,可能会引起这个错误,一般情况下它需要MFC的库先于C运行期函数库被链接,因此建议使用支持多线程的msvcrt.lib。

所以在使用第三方的库之前首先要知道它链接的是什么库,否则就可能造成LNK2005错误。

如果不得不使用第三方的库,可以尝试按下面所说的方法修改,但不能保证一定能解决问题,前两种方法是微软提供的:

A、选择VC菜单Project->

Settings->

Link->

Catagory选择Input,再在Ignorelibraries的Edit栏中填入你需要忽略的库,如:

Nafxcwd.lib;

Libcmtd.lib。

然后在Object/libraryModules的Edit栏中填入正确的库的顺序,这里需要你能确定什么是正确的顺序,呵呵,Godblessyou!

B、选择VC菜单Project->

Link页,然后在ProjectOptions的Edit栏中输入/verbose:

lib,这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。

C、选择VC菜单Project->

C/C++页,Catagory选择CodeGeneration后再在UserRuntimelibraray中选择MultiThreadDLL等其他库,逐一尝试。

关于编译器的相关处理过程,参考:

这就是我所遇到过的LNK2005错误的几种情况,肯定还有其他的情况也可能造成这种错误,所以我不希望你在看完这篇文章以后,再遇到LNK2005错误时候,不动脑筋的想对号入座的排除错误。

编程的过程就是一个思考的过程,所以还是多多开动你的头脑,那样收获会更多!

附录:

编译器处理相关

一.预处理器-编译器-汇编器-链接器

预处理器会处理相关的预处理指令,一般是以"

#"

开头的指令。

如:

#include"

xx.h"

#define等。

编译器把对应的*.cpp翻译成*.s文件(汇编语言)。

汇编器则处理*.s生成对应的*.o文件(obj目标文件)

最后链接器把所有的*.o文件链接成一个可执行文件(?

.exe)

1.部件:

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

*.h)及实现文件(如:

*.cpp)。

一般头文件会是放一些用来作声明的东东作为接口而存在的。

而实现文件主要是实现的具体代码。

2.编译单个文件:

记住IDE在bulid文件时只编译实现文件(如*.cpp)来产生obj,在vc下你可以对某个?

.cpp按下ctrl+f7单独编译它

生成对应一个?

.obj文件。

在编译?

.cpp时IDE会在?

.cpp中按顺序处理用#include包括进来的头文件

(如果该头文件中又#include有文件,同样会按顺序跟进处理各个头文件,如此递归。

3.内部链接与外部链接:

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

内部链接产生的符号只在本地?

.obj中可见,而外部链接的符号是所有*.obj之间可见的。

如:

用inline的是内部链接,在文件头中直接声明的变量、不带inline的全局函数都是外部链接。

在文件头中类的内部声明的函数(不带函数体)是外部链接,而带函数体一般会是内部链接(因为IDE会尽量把它作为内联函数)

认识内部链接与外部链接有什么作用呢?

下面用vc6举个例子:

//文件main.cpp内容:

voidmain(){}

//文件t1.cpp内容:

a.h"

voidTest1(){Foo();

}

//文件t2.cpp内容:

voidTest2(){Foo();

//文件a.h内容:

voidFoo(){}

好,用vc生成一个空的console程序(File-new-projects-win32consoleapplication),并关掉预编译选项开关

(project-setting-CagegoryrecompiledHeaders-Notusingprecompiledheaders)

现在你打开t1.cpp按ctrl+f7编译生成t1.obj通过

打开t2.cpp按ctrl+f7编译生成t2.obj通过

而当你链接时会发现:

Linking...

t2.obj:

errorLNK2005:

"

void__cdeclFoo(void)"

(?

Foo@@YAXXZ)alreadydefinedint1.obj

这是因为:

1.编译t1.cpp在处理到#include"

中的Foo时看到的Foo函数原型定义是外部链接的,所以在t1.obj中记录Foo符号是外部的。

2.编译t2.cpp在处理到#include"

中的Foo时看到的Foo函数原型定义是外部链接的,所以在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.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中加入:

classCFoo{};

CFoo*obj;

这时按f7进行build时出现:

classCFoo*obj"

obj@@3PAVCFoo@@A)alreadydefinedint1.obj

一个好办法就是在a.cpp中定义此变量(CFoo*obj,然后拷贝此定义到a.h文件中并在前面加上extern(externCFoo*obj

如此就可通过了。

当然extern也可以在任何调用此变量的位置之前声明,不过强烈建议不要这么作,因为到处作用extern,会

导致接口不统一。

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

b.将a.h中的定义修改成内部链接,即加上inline关键字,这时每个t1.obj和t2.obj都存放有一份Foo函数体,但它们不是外部

符号,所以不会被别的obj文件引用到,故不存在冲突。

另外我作了个实验来验证”vc是把是否是外部符号的标志记录在obj文件中的“(有点绕口)。

可以看看,如下:

(1)文件内容:

inlinevoidFoo(){}

(2)选t1.cpp按ctrl+f7单独编译,并把编译后的t1.obj修改成t1.obj_inline

(3)选t2.cpp按ctrl+f7单独编译,并把编译后的t2.obj修改成t2.obj_inline

(4)把除了t1.obj_inline及t2.obj_inline外的其它编译生成的文件删除。

(5)修改a.h内容为:

voidFoo(){},使之变为非内联函数作测试

(6)rebuildall所有文件。

这时提示:

Debug/cle.exe:

fatalerrorLNK1169:

oneormoremultiplydefinedsymbolsfound

(7)好,看看工程目录下的debug目录中会看到新生成的obj文件。

下面我们来手工链接看看,

打开菜单中的project-setting-Link,拷贝Projectoptions下的所有内容,如下:

kernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.libkernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.lib/nologo/subsystem:

console/incremental:

yes/pdb:

"

Debug/cle.pdb"

/debug/machine:

I386/out:

Debug/cle.exe"

/pdbtype:

sept

把它修改成:

Link.exekernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.libkernel32.libuser32.libgdi32.libwinspool.libcomdlg32.libadvapi32.libshell32.libole32.liboleaut32.libuuid.libodbc32.libodbccp32.lib/nologo/subsystem:

septDebug/t1.objDebug/t2.objDebug/main.obj

pause

注意前面多了Link.exe,后面多了Debug/t1.objDebug/t2.objDebug/main.obj以及

最后一个pause批处理命令,然后把它另存到工程目录(此目录下会看到debug目录)下起名为link.bat

运行它,就会看到:

Foo@@YAXXZ)alreadydefinedi

nt1.obj

很好,我们链接原来的obj文件得到的效果跟在vc中用rebuildall出来的效果一样。

那么现在如果

我们把备份出来的t1.obj_inline覆盖t1.obj而t2.obj_inline覆盖t2.obj再手动链接应该会是

不会出错的,因为原t1.obj_inline及t2.obj_inline中存放的是内部链接符号。

好运行Link.bat,果然

不出所料,链接成功了,看看debug目录下多出了一个exe文件。

这就说明了内或外符号在obj有标志标识!

上述为什么不用vc的f7build链接呢,因为文件时间改变了,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"

)”。

另外我们一般会尽量减少文件头之间的依赖关系,看下面:

2).减少部件之间的依赖性:

在1的基础上尽量把#include到的文件放在cpp中包括。

这就要求我们一般不要在头文件中直接引用其它变量的实现,而是把此引用搬到实现文件中。

//文件foo.h:

classCFoo{

voidFoo(){}

};

//文件test.h:

foo.h"

classCTest{

CFoo*m_pFoo;

public:

CTest():

m_pFoo(NULL){}

voidTest(){if(m_pFoo){m_pFoo->

Foo();

}}

...........

//文件test.cpp:

.....

如上文件test.h中我们其实可以#include"

移到test.cpp文件中。

因为CFoo*m_pFoo我们只想在部件CTest中用到,

而将来想用到CTest部件而包括test.h的其它部件没有必要见到foo.h接口,所以我们用前向声明修改原文件如下:

classCFoo;

CTest();

voidTest();

//........

//这里第一个放该部件自身对应的接口头文件

//该部件用到了foo.h

CTest:

:

m_pFoo(0){

m_pFoo=newCFoo;

}

voidCTest:

Test(){

if(m_pFoo){

m_pFoo->

//.....

//再加上main.cpp来测试:

//这里我们就不用见到#include"

CTesttest;

voidmain(){

test.Test();

3).双重包含卫哨:

在文件头中包括其它头文件时(如:

)建议也加上包含卫哨:

//test.h文件内容:

#ifndef__XX1_H_

xx1.h"

#endif

#ifndef__XX2_H_

xx2.h"

......

虽然我们已经在xx.h文件中开头已经加过,但是因为编译器在打开#include文件也

是需要时间的,如果在外部加上包含卫哨,对于很大的工程可以节省更多的编译时间。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 其它语言学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1