說明:出現這個錯誤,請你先檢查重複定義的變量是否是定義在了.h頭文件中,如果是,請您耐心的看完這篇文章,他會告訴你錯誤的根本原因。
如果你很着急,不想弄清楚原因,請直接按下面的方法更改:
假設重複定義的變量是int a,且你定義在了b.h,想作爲全局變量使用,那麼:
1.刪除b.h中的int a
2.在b.cpp中加入a的定義int a;
3.在b.h中加入 extern int a;
4.在要使用a的cpp文件中加入#include “b.h”
1.引言
學習計算機那麼久了,也編程這麼久了,但是一直都沒有徹底弄清楚頭文件互相包含時,爲什麼有時候會出錯,出現重複定義,有時候確又能夠正常編譯鏈接,今天仔細研究了一下這個問題,將結論記錄下來。
2.編譯
編譯,大家都知道,最後就會產生一個.o文件,我以前估計就理解到這個水平。於是我常常想不通一個問題,比如MFC的一個大工程,裏面有a.cpp,a.h,b.cpp,b.h,那他們是合在一起編譯嗎?那互相包含會不會出錯?
結論:編譯是針對一個文件來說的,比如有a.cpp,a.h,b.cpp,b.h,他們之間的編譯是沒有關係的,a.cpp和a.h會產生一個a.o,他的編譯和b完全沒有關係,同樣b.cpp和b.h產生的b.o和a也沒有關係(除了include包含的情況)。
3.鏈接
鏈接就很有可能是很多.o文件一起鏈接,組成一個可執行文件,例如g++ a.cpp b.cpp -o c,這樣就是先單獨編譯了a.cpp和b.cpp並將產生的a.o,b.o鏈接爲c
4.頭文件包含的問題
我們都知道,ifndef是爲了防止頭文件重複包含,比如
a.h:
#include "b.h"
#include "c.h"
b.h:
#include "c.h"
c.h:
//內容......
這樣的話,我們編譯a.cpp肯定會出現問題,因爲a.h裏面包含了2次c.h,而c.h裏面沒有加入#ifndef之類的語句
所以我們需要將c.h改爲下面的形式:
c.h
#ifndef _C_HEADER
#define _C_HEADER
//.....內容
#endif
這樣的話,編譯a.cpp就沒錯了,但是我們要注意,#ifndef只是針對某一個文件,而不是一個工程,比如你在a.h裏面包含了c.h,在b.h裏面也包含了c.h,這樣不管你的c.h有沒有加入#ifndef,你的c.h文件在a.h和b.h當中都會展開,而不是說a和b因爲是一個工程,所以只展開一次。
5.extern的用法
首先,頭文件.h中不適宜定義變量,我們都知道定義全局變量的常規使用方法是在.cpp文件中定義變量,在.h文件中用extern申明,這是爲什麼呢?看下面的例子:
比如在MFC的一個大工程中,我想定義一個全局變量int a,因爲所有的文件都包含stdafx.h,我們考慮寫在裏面
第一種寫法:
stdafx.h:
int a
//........
a.cpp
#include "stdafx.h"
extern int a;
a = 100;
//.....
b.cpp
#include "stdafx.h"
extern int a;
a = 200;
//.......
接下來,
編譯,沒錯耶!!!好開心
鏈接,怎麼出錯了?!!!重複定義a?好難過…
我就是這麼難過…終於弄除了原因:
原因:在編譯的時候,由於是一個一個文件爲單位,所以a.cpp a.h被單獨編譯產生a.o,這樣在編譯的時候,由於他包含了stdafx.h,所以在a.o中,已經有int a的定義了
而b.cpp b.h也是被單獨編譯的,產生b.o裏面也有一個int a的定義,所以出現了重複定義的錯誤。
正確的寫法
stdafx.cpp
int a;
stdafx.h
extern int a;
a.cpp
#include"stdafx.h"
a=100;
//......
b.cpp
#include"stdafx.h"
//......
a= 200;
這樣會發現編譯鏈接都正確,這是爲什麼呢?
因爲在編譯的時候,由於mfc會自己默認編譯stdafx.h和stdafx.cpp產生stdafx.o文件,然後我們的a.cpp編譯的時候包括了stdafx.h,但是stdafx.h裏面只有extern a,這就說明,我們在下面使用到的a都是在其他文件中定義的,同樣的b.cpp編譯時,也不會定義a,所以編譯出來的a.o和b.o都不包含a的定義。最後鏈接,鏈接的時候,由於這是一個工程,所以mfc會把stdafx.o,a.o,b.o鏈接在一起,這樣我們的a就定義了一次,在stdafx.o中。
看到這裏,你可能會覺得奇怪,我都不知道我按下vc6.0的編譯按鈕,是編譯了那麼多文件,按下鏈接按鈕,又是把所有文件鏈接到一起了,這些可能需要進一步瞭解makefile之類的東西。
6.結論
1.編譯是針對一個一個文件來說的,而鏈接則是針對一個工程所有的.o文件而言的。
2.#ifndef只是對防止一個文件的重複編譯有效
3.全局變量最好在.cpp文件中定義,在.h文件中加上extern申明,因爲在.h文件中定義,容易在鏈接時造成變量重定義。
4.注意mfc中會自動編譯stdafx,並將stdafx.o加入到工程中一起鏈接.