先是源文件代碼(爲方便實驗找出問題而簡化的相關代碼):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便可