先是源文件代码(为方便实验找出问题而简化的相关代码):main.c程序员
01 #include <windows.h>
02 #include "msgqueue.h"
03 #pragma comment(linker, "/subsystem:windows /RELEASE ")
04
05 extern QUEUE_INSTANCE Queue_Instance; //关键语句A
06
07 int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
08 {
09 Queue_Instance.iMsgCount=0;
10 Queue_Instance.iMsgSequence=0;
11 return 0;
12 }windows
下面第二个源文件:MsgQueue.h多线程
01 #define MAX_CHAR 256
02 #define QUEUE_SIZE 500
03 typedef struct {
04 int iMsgId;
05 char szSender[12];
06 char szContent[MAX_CHAR];
07 } MSG_QUEUE_ITEM;
08
09 typedef struct {
10 int iMsgCount;
11 int iMsgSequence;
12 MSG_QUEUE_ITEM MsgQueue[QUEUE_SIZE];
13 }QUEUE_INSTANCE;
14
15 QUEUE_INSTANCE Queue_Instance={0};//关键语句Bapp
下面第三个源文件:MsgQueue..c编辑器
01 #include <windows.h>
02 #include "msgqueue.h"
03 ide
04 //Queue_Instance={0};
05 void InsertMsgQueue(char *lpszSender,char *lpszContent)
06 {
07 int i;
08
09 函数
10 if (Queue_Instance.iMsgCount>=QUEUE_SIZE)//里面要用Queue_Instance实例
11 {
12 for (i=0;i<QUEUE_SIZE-1;i++)
13 {
14 Queue_Instance.MsgQueue[i]=Queue_Instance.MsgQueue[i+1];
15 }
16 Queue_Instance.iMsgCount=i;
17 }
18 }工具
先发布官方解释及处理方案:ui
1.Linker Tools Error LNK1169this
one or more multiply defined symbols found
The build failed due to multiple definitions of one or more symbols. This error is preceded by error LNK2005.
The /FORCE or /FORCE:MULTIPLE option overrides this error.
也就是"在 Project/Setting/Link/General中的 Project Options: 加入 /FORCE:MULTIPLE便可"
2.Linker Tools Error LNK2005
symbol already defined in object
The given symbol, displayed in its decorated form, was multiply defined.
Tips
One of the following may be a cause:
The most common cause of this error is accidentally linking with both the single-threaded and multithreaded libraries. Ensure that the application project file includes only the appropriate libraries and that any third-party libraries have appropriately created single-threaded or multithreaded versions.//到 "Project属性" -> "C/C++" -> "代码生成(code generation)" -> "运行时库(run-time library)" 项下设置应用程序为多线程,或单线程
The given symbol was a packaged function (created by compiling with /Gy) and was included in more than one file but was changed between compilations. Recompile all files that include thesymbol. //C语言提供了一种将多个目标文件打包成一个文件的机制,这就是静态程序库(static library)。程序库为开发者带来了方便,但同时也是某些混乱的根源......略去
The given symbol was defined differently in two member objects in different libraries, and both member objects were used.//不一样库中对同一全局对象有不一样定义.
An absolute was defined twice, with a different value in each definition. //.//不一样库中对同一全局对象有不一样值.
This error is followed by fatal error LNK1169.
看玩MSDN的箴言,抄家伙,改成多线程运行时进行编译,获得结果:失败
强制输出文件,获得结果:不算失败,但没解决多实例问题:warning LNK4088: image being generated due to /FORCE option; image may not run,不保险啊.
看了N篇文章,没有对策.代码少,无心间把QUEUE_INSTANCE Queue_Instance={0};//关键语句B
中的初始化部分={0}; 移到另外一个文件MsgQueue.c,进行编译,获得结果:OK
实验收获:对全局数据的初始化要放在源文件中,不能放在头文件中
如下为转载网上的经典分析文章,借鉴下:
fatal error LNK1169: one or more multiply defined symbols found
属于编译联接的常见问题之一,缘由是在不一样的源文件重复定义变量。好比project1有2个.c或.cpp文件,假设为a.c,b.c,若是
1:定义了相同名字的变量;
2:包含了一样的头文件(其中定义了非局部变量);
这样在编译生成的a.obj,b.obj文件中都会为为这个同一变量 分配空间,linker会做名称检查,若是出现相同名字就会出现:
fatal error LNK1169: one or more multiply defined symbols found
解决方法:对于第一种状况,用external关键字屏蔽其它重复定义便可; 实际上状况2更隐蔽常见,只能避免定义非局部变量。
//////////////////////////////////////////////////////////////
你们都知道,从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码,而后由汇编器 (assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中;(2)连接器(linker)将一个个的目标文件(或许还会有若干程序库)连接在一块儿生成一个完整的可执行文件。
编译器编译源文件时会把源文件的全局符号(global symbol)分红强(strong)和弱(weak)两类传给汇编器,而随后汇编器则将强弱信息编码并保存在目标文件的符号表中。那么何谓强弱呢?编译器认为函数与初始化了的全局变量都是强符号,而未初始化的全局变量则成了弱符号。好比有这么个源文件:
extern int errorno;
int buf[2] = {1,2};
int *p;
int main()
{
return 0;
}
其中main、buf是强符号,p是弱符号,而errorno则非强非弱,由于它只是个外部变量的使用声明。
有了强弱符号的概念,咱们就能够看看连接器是如何处理与选择被屡次定义过的全局符号:
规则1: 不容许强符号被屡次定义(即不一样的目标文件中不能有同名的强符号);
规则2: 若是一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么选择强符号;
规则3: 若是一个符号在全部目标文件中都是弱符号,那么选择其中任意一个;
由上可知多个目标文件不能重复定义同名的函数与初始化了的全局变量,不然必然致使LNK2005和LNK1169两种连接错误。但是,有的时候咱们并无在本身的程序中发现这样的重定义现象,却也遇到了此种连接错误,这又是何解?嗯,问题稍微有点儿复杂,容我慢慢道来。
众所周知,ANSI C/C++ 定义了至关多的标准函数,而它们又分布在许多不一样的目标文件中,若是直接以目标文件的形式提供给程序员使用的话,就须要他们确切地知道哪一个函数存在于哪一个 目标文件中,而且在连接时显式地指定目标文件名才能成功地生成可执行文件,显然这是一个巨大的负担。因此C语言提供了一种将多个目标文件打包成一个文件的 机制,这就是静态程序库(static library)。开发者在连接时只需指定程序库的文件名,连接器就会自动到程序库中寻找那些应用程序确实用到的目标模块,并把(且只把)它们从库中拷贝 出来参与构建可执行文件。几乎全部的C/C++开发系统都会把标准函数打包成标准库提供给开发者使用(有不这么作的吗?)。
程序库为开发者带来了方便,但同时也是某些混乱的根源。咱们来看看连接器是如何解析(resolve)对程序库的引用的。
在符号解析(symbol resolution)阶段,连接器按照全部目标文件和库文件出如今命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合:(1)集合E是将被合并到一块儿组成可执行文件的全部目标文件集合;(2)集合U是未解析符号(unresolved symbols,好比已经被引用可是还未被定义的符号)的集合;(3)集合D是全部以前已被加入到E的目标文件定义的符号集合。一开始,E、U、D都是空的。
(1): 对命令行中的每个输入文件f,连接器肯定它是目标文件仍是库文件,若是它是目标文件,就把f加入到E,并把f中未解析的符号和已定义的符号分别加入到U、D集合中,而后处理下一个输入文件。
(2): 若是f是一个库文件,连接器会尝试把U中的全部未解析符号与f中各目标模块定义的符号进行匹配。若是某个目标模块m定义了一个U中的未解析符号,那么就把 m加入到E中,并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的全部目标模块重复这个过程直至到达一个不动点(fixed point),此时U和D再也不变化。而那些未加入到E中的f里的目标模块就被简单地丢弃,连接器继续处理下一输入文件。
(3): 若是处理过程当中往D加入一个已存在的符号,或者当扫描完全部输入文件时U非空,连接器报错并中止动做。不然,它把E中的全部目标文件合并在一块儿生成可执行文件。
VC带的编译器名字叫cl.exe,它有这么几个与标准程序库有关的选项: /ML、/MLd、/MT、/MTd、/MD、/MDd。这些选项告诉编译器应用程序想使用什么版本的C标准程序库。/ML(缺省选项)对应单线程静态版 的标准程序库(libc.lib);/MT对应多线程静态版标准库(libcmt.lib),此时编译器会自动定义_MT宏;/MD对应多线程DLL版 (导入库msvcrt.lib,DLL是msvcrt.dll),编译器自动定义_MT和_DLL两个宏。后面加d的选项都会让编译器自动多定义一个 _DEBUG宏,表示要使用对应标准库的调试版,所以/MLd对应调试版单线程静态标准库(libcd.lib),/MTd对应调试版多线程静态标准库 (libcmtd.lib),/MDd对应调试版多线程DLL标准库(导入库msvcrtd.lib,DLL是msvcrtd.dll)。虽然咱们的确在 编译时明白无误地告诉了编译器应用程序但愿使用什么版本的标准库,但是当编译器干完了活,轮到连接器开工时它又如何得知一个个目标文件到底在思念谁?为了 传递相思,咱们的编译器就干了点秘密的勾当。在cl编译出的目标文件中会有一个专门的区域(关心这个区域到底在文件中什么地方的朋友能够参考COFF和 PE文件格式)存放一些指导连接器如何工做的信息,其中有一种就叫缺省库(default library),这些信息指定了一个或多个库文件名,告诉连接器在扫描的时候也把它们加入到输入文件列表中(固然顺序位于在命令行中被指定的输入文件之 后)。说到这里,咱们先来作个小实验。写个顶顶简单的程序,而后保存为main.c :
int main() { return 0; }
用下面这个命令编译main.c(什么?你从不用命令行来编译程序?这个......) :
cl /c main.c
/c是告诉cl只编译源文件,不用连接。由于/ML是缺省选项,因此上述命令也至关于: cl /c /ML main.c 。若是没什么问题的话(要出了问题才是活见鬼!固然除非你的环境变量没有设置好,这时你应该去VC的bin目录下找到vcvars32.bat文件而后运 行它。),当前目录下会出现一个main.obj文件,这就是咱们可爱的目标文件。随便用一个文本编辑器打开它(是的,文本编辑器,大胆地去作别惧怕), 搜索"defaultlib"字符串,一般你就会看到这样的东西: "-defaultlib:LIBC -defaultlib:OLDNAMES"。啊哈,没错,这就
是保存在目标文件中的缺省库信息。咱们的目标文件显然指定了两个缺省库,一个是单线程静态版标准库libc.lib(这与/ML选项相符),另一个是oldnames.lib(它是为了兼容微软之前的C/C++开发系统)。
VC的连接器是link.exe,由于main.obj保存了缺省库信息,因此能够用
link main.obj libc.lib
或者
link main.obj
来生成可执行文件main.exe,这两个命令是等价的。可是若是你用
link main.obj libcd.lib
的话,连接器会给出一个警告: "warning LNK4098: defaultlib "LIBC" conflicts with use of other libs; use /NODEFAULTLIB:library",由于你显式指定的标准库版本与目标文件的缺省值不一致。一般来讲,应该保证连接器合并的全部目标文件指定 的缺省标准库版本一致,不然编译器必定会给出上面的警告,而LNK2005和LNK1169连接错误则有时会出现有时不会。那么这个有时究竟是何时? 呵呵,别着急,下面的一切正是为喜欢追根究底的你准备的。
建一个源文件,就叫mylib.c,内容以下:
#i nclude
void foo()
{
printf("%s","I am from mylib!/n");
}
用
cl /c /MLd mylib.c
命令编译,注意/MLd选项是指定libcd.lib为默认标准库。lib.exe是VC自带的用于将目标文件打包成程序库的命令,因此咱们能够用
lib /OUT:my.lib mylib.obj
将mylib.obj打包成库,输出的库文件名是my.lib。接下来把main.c改为:
void foo();
int main()
{
foo();
return 0;
}
用
cl /c main.c
编译,而后用
link main.obj my.lib
进行连接。这个命令可以成功地生成main.exe而不会产生LNK2005和LNK1169连接错误,你仅仅是获得了一条警告信息:"warning LNK4098: defaultlib "LIBCD" conflicts with use of other libs; use /NODEFAULTLIB:library"。咱们根据前文所述的扫描规则来分析一下连接器此时作了些啥。
一开始E、U、D都是空集,连接器首先扫描到main.obj,把它加入E集合,同时把未解析的foo加入U,把main加入D,并且由于 main.obj的默认标准库是libc.lib,因此它被加入到当前输入文件列表的末尾。接着扫描my.lib,由于这是个库,因此会拿当前U中的全部 符号(固然如今就一个foo)与my.lib中的全部目标模块(固然也只有一个mylib.obj)依次匹配,看是否有模块定义了U中的符号。结果 mylib.obj确实定义了foo,因而它被加入到E,foo从U转移到D,mylib.obj引用的printf加入到U,一样 地,mylib.obj指定的默认标准库是libcd.lib,它也被加到当前输入文件列表的末尾(在libc.lib的后面)。不断地在my.lib库 的各模块上进行迭代以匹配U中的符号,直到U、D都再也不变化。很明显,如今就已经到达了这么一个不动点,因此接着扫描下一个输入文件,就是 libc.lib。连接器发现libc.lib里的printf.obj里定义有printf,因而printf从U移到D,而printf.obj被加 入到E,它定义的全部符号加入到D,它里头的未解析符号加入到U。连接器还会把每一个程序都要用到的一些初始化操做所在的目标模块(好比crt0.obj 等)及它们所引用的模块(好比malloc.obj、free.obj等)自动加入到E中,并更新U和D以反应这个变化。事实上,标准库各目标模块里的未 解析符号均可以在库内其它模块中找到定义,所以当连接器处理完libc.lib时,U必定是空的。最后处理libcd.lib,由于此时U已经为空,因此 连接器会抛弃它里面的全部目标模块从而结束扫描,而后合并E中的目标模块并输出可执行文件。
上文描述了虽然各目标模块指定了不一样版本的缺省标准库但仍然连接成功的例子,接下来你将目击由于这种不严谨而致使的悲惨失败。
修改mylib.c成这个样子:
#i nclude
void foo()
{
// just a test , don"t care memory leak
_malloc_dbg( 1, _NORMAL_BLOCK, __FILE__, __LINE__ );
}
其中_malloc_dbg不是ANSI C的标准库函数,它是VC标准库提供的malloc的调试版,与相关函数配套能帮助开发者抓各类内存错误。使用它必定要定义_DEBUG宏,不然预处理器会把它自动转为malloc。继续用
cl /c /MLd mylib.c
lib /OUT:my.lib mylib.obj
编译打包。当再次用
link main.obj my.lib
进行连接时,咱们看到了什么?天哪,一堆的LNK2005加上个贵为"fatal error"的LNK1169垫底,固然还少不了那个LNK4098。连接器是否是疯了?不,你冤枉可怜的连接器了,我拍胸脯保证它但是一直在尽心尽责地照章办事。
一开始E、U、D为空,连接器扫描main.obj,把它加入E,把foo加入U,把main加入D,把libc.lib加入到当前输入文件列表的末尾。 接着扫描my.lib,foo从U转移到D,_malloc_dbg加入到U,libcd.lib加到当前输入文件列表的尾部。而后扫描 libc.lib,这时会发现libc.lib里任何一个目标模块都没有定义_malloc_dbg(它只在调试版的标准库中存在),因此不会有任何一个 模块由于_malloc_dbg而加入E,可是每一个程序都要用到的初始化模块(如crt0.obj等)及它们所引用的模块(好比malloc.obj、 free.obj等)仍是会自动加入到E中,同时U和D被更新以反应这个变化。当连接器处理完libc.lib时,U只剩_malloc_dbg这一个符 号。最后处理libcd.lib,发现dbgheap.obj定义了_malloc_dbg,因而dbgheap.obj加入到E,它里头的未解析符号加 入U,它定义的全部其它符号也加入D,这时灾难便来了。以前malloc等符号已经在D中(随着libc.lib里的malloc.obj加入E而加入 的),而dbgheap.obj又定义了包括malloc在内的许多同名符号,这引起了重定义冲突,连接器只好中断工做并报告错误。
如今咱们该知道,连接器彻底没有责任,责任在咱们本身的身上。是咱们粗心地把缺省标准库版本不一致的目标文件(main.obj)与程序库 (my.lib)连接起来,致使了大灾难。解决办法很简单,要么用/MLd选项来重编译main.c;要么用/ML选项重编译mylib.c。
在上述例子中,咱们拥有库my.lib的源代码(mylib.c),因此能够用不一样的选项从新编译这些源代码并再次打包。可若是使用的是第三方的库,它并 没有提供源代码,那么咱们就只有改变本身程序的编译选项来适应这些库了。可是如何知道库中目标模块指定的默认库呢?其实VC提供的一个小工具即可以完成任 务,这就是dumpbin.exe。运行下面这个命令
dumpbin /DIRECTIVES my.lib
而后在输出中找那些"Linker Directives"引导的信息,你必定会发现每一处这样的信息都会包含若干个相似"-defaultlib:XXXX"这样的字符串,其中XXXX便表明目标模块指定的缺省库名。
知道了第三方库指定的默认标准库,再用合适的选项编译咱们的应用程序,就能够避免LNK2005和LNK1169连接错误。喜欢IDE的朋友,你同样能够到 "Project属性" -> "C/C++" -> "代码生成(code generation)" -> "运行时库(run-time library)" 项下设置应用程序的默认标准库版本,这与命令行选项的效果是同样的。
终极解决办法:在 Project/Setting/Link/General中的 Project Options: 加入 /FORCE:MULTIPLE便可