(1)編譯單元(模塊)
在VC或VS上編寫完代碼,,點擊編譯按鈕準(zhǔn)備生成exe文件時,,編譯器做了兩步工作:
第一步,,將每個.cpp(.c)和相應(yīng)的.h文件編譯成obj文件;
第二步,,將工程中所有的obj文件進(jìn)行LINK,,生成最終.exe文件。
那么,,錯誤可能在兩個地方產(chǎn)生:
一個,,編譯時的錯誤,這個主要是語法錯誤,;
一個,,鏈接時的錯誤,主要是重復(fù)定義變量等,。
編譯單元指在編譯階段生成的每個obj文件,。
一個obj文件就是一個編譯單元。
一個.cpp(.c)和它相應(yīng)的.h文件共同組成了一個編譯單元,。
一個工程由很多編譯單元組成,,每個obj文件里包含了變量存儲的相對地址等。
(2)聲明與定義 函數(shù)或變量在聲明時,,并沒有給它實際的物理內(nèi)存空間,,它有時候可保證你的程序編譯通過;
函數(shù)或變量在定義時,,它就在內(nèi)存中有了實際的物理空間,。
如果你在編譯單元中引用的外部變量沒有在整個工程中任何一個地方定義的話,那么即使它在編譯時可以通過,,在連接時也會報錯,,因為程序在內(nèi)存中找不到這個變量。
函數(shù)或變量可以聲明多次,,但定義只能有一次,。
(3) extern作用
作用一:當(dāng)它與"C"一起連用時,,如extern "C" void fun(int a, int b);,,則編譯器在編譯fun這個函數(shù)名時按C的規(guī)則去翻譯相應(yīng)的函數(shù)名而不是C++的。
作用二:當(dāng)它不與"C"在一起修飾變量或函數(shù)時,,如在頭文件中,,extern int g_nNum;,它的作用就是聲明函數(shù)或變量的作用范圍的關(guān)鍵字,,其聲明的函數(shù)和變量可以在本編譯單元或其他編譯單元中使用,。
即B編譯單元要引用A編譯單元中定義的全局變量或函數(shù)時,B編譯單元只要包含A編譯單元的頭文件即可,,在編譯階段,,B編譯單元雖然找不到該函數(shù)或變量,但它不會報錯,它會在鏈接時從A編譯單元生成的目標(biāo)代碼中找到此函數(shù),。
(4)全局變量(extern)
有兩個類都需要使用共同的變量,,我們將這些變量定義為全局變量。比如,,res.h和res.cpp分別來聲明和定義全局變量,,類ProducerThread和ConsumerThread來使用全局變量。(以下是QT工程代碼)
[cpp] view plaincopy
上述代碼中g(shù)_nDataSize、g_nBufferSize為全局常量,,其他為全局變量,。
[cpp] view plaincopy
在其他編譯單元中使用全局變量時只要包含其所在頭文件即可。
[cpp] view plaincopy
也可以把全局變量的聲明和定義放在一起,,這樣可以防止忘記了定義,,如上面的extern char g_szBuffer[g_nBufferSize]; 然后把引用它的文件中的#include "res.h"換成extern char g_szBuffer[];。 但是這樣做很不好,,因為你無法使用#include "res.h"(使用它,,若達(dá)到兩次及以上,就出現(xiàn)重定義錯誤,;注:即使在res.h中加#pragma once,,或#ifndef也會出現(xiàn)重復(fù)定義,因為每個編譯單元是單獨的,,都會對它各自進(jìn)行定義),,那么res.h聲明的其他函數(shù)或變量,你也就無法使用了,,除非也都用extern修飾,,這樣太麻煩,所以還是推薦使用.h中聲明,,.cpp中定義的做法,。
(5)靜態(tài)全局變量(static) 注意使用static修飾變量,就不能使用extern來修飾,,即static和extern不可同時出現(xiàn),。
static修飾的全局變量的聲明與定義同時進(jìn)行,即當(dāng)你在頭文件中使用static聲明了全局變量,,同時它也被定義了,。
static修飾的全局變量的作用域只能是本身的編譯單元。在其他編譯單元使用它時,,只是簡單的把其值復(fù)制給了其他編譯單元,,其他編譯單元會另外開個內(nèi)存保存它,,在其他編譯單元對它的修改并不影響本身在定義時的值。即在其他編譯單元A使用它時,,它所在的物理地址,,和其他編譯單元B使用它時,它所在的物理地址不一樣,,A和B對它所做的修改都不能傳遞給對方,。
多個地方引用靜態(tài)全局變量所在的頭文件,不會出現(xiàn)重定義錯誤,,因為在每個編譯單元都對它開辟了額外的空間進(jìn)行存儲,。
以下是Windows控制臺應(yīng)用程序代碼示例:
[cpp] view plaincopy
[cpp] view plaincopy
[cpp] view plaincopy
[cpp] view plaincopy
[cpp] view plaincopy
[cpp] view plaincopy
[cpp] view plaincopy
運行結(jié)果如下:
按我們的直觀印象,認(rèn)為fun1()和fun2()輸出的結(jié)果都為abcdef,,可實際上fun2()輸出的確是初始值,。然后我們再跟蹤調(diào)試,發(fā)現(xiàn)res,、test1,、test2中g(shù)_szBuffer的地址都不一樣,分別為0x0041a020,、0x0041a084,、0x0041a040,這就解釋了為什么不一樣,。
注:一般定義static 全局變量時,,都把它放在.cpp文件中而不是.h文件中,這樣就不會給其他編譯單元造成不必要的信息污染,。
(6)全局常量(const)
const單獨使用時,,其特性與static一樣(每個編譯單元中地址都不一樣,不過因為是常量,,也不能修改,,所以就沒有多大關(guān)系)。
const與extern一起使用時,,其特性與extern一樣,。
[cpp] view plaincopy
|
|