近日在寫代碼,各個.cpp源文件編譯時沒有問題,將*.o進行鏈接時,出現了許多multiple definition of XXX的鏈接錯誤。於是在網上搜索了一番,結合自己的代碼包含邏輯,最終發現了問題,記載如下:
一、問題描述:
Threadpool.h:
聲明了一些函數原型和一些全局變量,這些標示符是沒有帶extern關鍵字的。
Threadpool.cpp:
該cpp文件對Threadpool.h中函數進行定義,變量給出初始值。
單獨編譯g++ -g -c Threadpool.cpp不會有問題。
Main.cpp:
我在Main.cpp中需要用到Threadpool.cpp中定義好的函數以及變量,所以我在Main.cpp中#include "Threadpool.h"。
單獨編譯g++ -g -c Main.cpp 也沒有問題。
當進行鏈接時:g++ -o Main Threadpool.o Main.o -lpthread
就會出現:multiple definition of XXX
二、題產生的原因:
C/C++中一個.c和.cpp文件以及其include到的頭文件稱為一個編譯單元,該單元是獨立編譯的。一個標示符可以多次聲明,但是只能夠定義一次。
為什么Threadpool.cpp和Main.cpp都包含Threadpool.h,在鏈接階段會出現重復定義的符號呢?真正的問題出在頭文件標示符。
Threadpool.h:
int a;
double d;
int fnu();
//etc
在Threadpool.cpp中#include時被預處理器處理之后,出現了一次定義。
int a;
double d;
int fnu();
在Main.cpp中#include同樣被預處理器處理之后:再一次出現了定義。
int a;
double d;
int fnu();
為什么編譯階段不會出現問題呢?
執行g++ -g -c Threadpool.cpp時,對應的.cpp文件將會被編譯成目標文件,目標文件含有一系列Threadpool.h中定義過的標示符。
執行g++ -g -c Main.cpp時,對應的.cpp文件也將會被編譯成目標文件,目標文件同樣含有一系列Threadpool.h中定義過的標示符。
上述二者被獨立的編譯成目標文件,所以編譯不會有問題。
我們可以這樣想象-c每一個源文件時,相當於一條有管道包圍的縱向水流,二者互不干擾。當-o鏈接時兩條原本相互獨立的水管橫向流了,所有就出現了重復的元素。所以當進行鏈接時:g++ -o Main Threadpool.o Main.o -lpthread。就會出現重復定義的標示符。重復定義的標示符在這里只是變量,函數不會。因為函數確實只在.cpp中定義了一次,多次聲明是沒有問題的,而變量確實出現了兩次定義。
三、我的解決方案:
不在頭文件中定義全局變量,頭文件中只含有函數的原型聲明,而將所有的全局變量放入Threadpool.cpp中,當Main.cpp需要用到這些全局變量時,extern一下,告訴編譯器這些變量是在其他源文件中定義的。
得到的啟示:
(1)最好不要在頭文件中定義全局變量。
(2)在C/C++中分清楚標示符的聲明(declared)和定義(definition)的區
別很重要。
(3)該問題與沒有用#ifndef,#define,#endif產生問題的原因是不一樣的。
非常好的一篇blog,我在寫代碼的時候也碰到了類似的問題,其實也是全局變量和函數的重復定義的問題!!!但是我的例子比較復雜,不好描述,而作者的例子清晰易理解,故轉載過來。另外這篇文章最令我受益的一句話是:
C/C++中一個.c和.cpp文件以及其include到的頭文件稱為一個編譯單元,該單元是獨立編譯的。一個標示符可以多次聲明,但是只能夠定義一次。
文章轉載自:http://blog.csdn.net/luo6620378xu/article/details/8511312