說(shuō)到這個(gè)問(wèn)題,先得說(shuō)說(shuō)VC對(duì)Debug和Release的定義。
實(shí)際上,Debug和Release只是VC對(duì)編譯選項(xiàng)的預(yù)定義而已,如果我們?cè)敢猓覀兺耆梢园袲ebug和Release的行為完全顛倒過(guò)來(lái)。
當(dāng)然,我們也可以自己定義一組編譯選項(xiàng),然后命名為ABC,DEF等。當(dāng)然,在習(xí)慣上,我們?nèi)匀桓敢馐褂肰C已經(jīng)定義好的名稱。
既然Debug和Release僅僅是編譯選項(xiàng)的不同,那么為什么要區(qū)分Debug和Release版本呢?
Debug和Release,在我看來(lái)主要是針對(duì)其面向的目標(biāo)不同的而進(jìn)行區(qū)分的。
Debug通常稱為調(diào)試版本,通過(guò)一系列編譯選項(xiàng)的配合,編譯的結(jié)果通常包含調(diào)試信息,而且不做任何優(yōu)化,以為開(kāi)發(fā)人員提供強(qiáng)大的應(yīng)用程序調(diào)試能力。
而Release通常稱為發(fā)布版本,是為用戶使用的,一般客戶不允許在發(fā)布版本上進(jìn)行調(diào)試。所以不保存調(diào)試信息,同時(shí),它往往進(jìn)行了各種優(yōu)化,以期達(dá)到代碼小和速度優(yōu)。為用戶的使用提供便利。
下面僅就默認(rèn)的Debug和Release版本的選項(xiàng)進(jìn)行比較,詳細(xì)的編譯選項(xiàng)可以看MSDN的說(shuō)明。
我們將默認(rèn)的Debug和Release的選項(xiàng)設(shè)置進(jìn)行比較,過(guò)濾掉相同設(shè)置,主要的不同如下:
編譯選項(xiàng):/Od /D "_DEBUG" /Gm /RTC1 /MDd /Fo"Debug\\" /ZI
鏈接選項(xiàng):/OUT:"D:\MyProject\logging\Debug\OptionTest.dll" /INCREMENTAL
默認(rèn)的Release設(shè)置如下:
編譯選項(xiàng):/O2 /GL /D "NDEBUG" /FD /MD /Fo"Release\\" /Zi
鏈接選項(xiàng):/OUT:"D:\MyProject\logging\Release\OptionTest.dll" /INCREMENTAL:NO
MDd
與
MD
首先,Debug版本使用調(diào)試版本的運(yùn)行時(shí)庫(kù)(/MDd選項(xiàng)),Relase版本則使用的是發(fā)布版本的運(yùn)行時(shí)庫(kù)(vcrt.dll)。其區(qū)別主要在于運(yùn)行時(shí)的性能影響。調(diào)試版本的運(yùn)行時(shí)庫(kù)包含了調(diào)試信息,并采用了一些保護(hù)機(jī)制以幫助發(fā)現(xiàn)錯(cuò)誤,也因此,其性能不如發(fā)布版本。編譯器提供的Runtime Library很穩(wěn)定,不會(huì)造成Release版本錯(cuò)誤,倒是由于Debug版本的Runtime Library加強(qiáng)了對(duì)錯(cuò)誤的檢測(cè),如堆內(nèi)存分配檢查等,反而會(huì)報(bào)告錯(cuò)誤,應(yīng)當(dāng)指出,如果Debug有錯(cuò)誤,而Release版本正常,程序肯定是有Bug的,只是我們還沒(méi)有發(fā)現(xiàn)。
ZI
與
Zi
其次,/ZI選項(xiàng)與/Zi選項(xiàng)。通過(guò)使用/ZI選項(xiàng),可以在調(diào)試過(guò)程修改代碼而不需要重新編譯。這是個(gè)調(diào)試的好幫手,可如果我們使用Release版本,這將變得不可行。
Od
與
O2
/O2與/Od選項(xiàng):Od是關(guān)閉編譯器優(yōu)化,普遍用于Debug版本。而O2選項(xiàng)是創(chuàng)建快速代碼,這當(dāng)然是Release版本的不二選擇。
RTCx
選項(xiàng)
/RTCx選項(xiàng),這個(gè)選項(xiàng)比較強(qiáng),它可以讓編譯器插入動(dòng)態(tài)檢測(cè)代碼以幫助你檢測(cè)程序中的錯(cuò)誤。比如,它會(huì)將局部變量初始化為非零值。包括用0xCC初始化所有自動(dòng)變量,0xCD初始化堆中分配的內(nèi)存(即new的內(nèi)存),使用0xDD填充被釋放的內(nèi)存(即delete的內(nèi)存),0xFD初始化受保護(hù)的內(nèi)存(debug版在動(dòng)態(tài)分配內(nèi)存的前后加入保護(hù)內(nèi)存以防止越界訪問(wèn))。這樣做的好處是這些值都很大,一般不可能作為指針,考試,大提示作為數(shù)值也很少用到,而且這些值很容易辯認(rèn),因此有利于在Debug版本中發(fā)現(xiàn)Release版才會(huì)遇到的錯(cuò)誤。
另外,通過(guò)函數(shù)指針調(diào)用函數(shù)時(shí),會(huì)通過(guò)檢查棧指針驗(yàn)證函數(shù)調(diào)用的匹配性(防止原型不匹配)。
使用/RTCx選項(xiàng)會(huì)造成Debug版本出錯(cuò),而Release版本正常的現(xiàn)象,因?yàn)镽elease版中未初始化的變量是隨機(jī)的,很可能使指針指向了有效但是錯(cuò)誤的地址,從而掩蓋了錯(cuò)誤。
說(shuō)了這么多好處,這個(gè)編譯選項(xiàng)有個(gè)限制:那就是只能在/Od選項(xiàng)下使用。
Gm
,
INCREMENTAL or NO
編譯選項(xiàng)中的Gm和鏈接選項(xiàng)中的INCREMENTAL都只為一個(gè)目的,加快編譯速度。我們經(jīng)常遇上這樣的問(wèn)題,只修改了一個(gè)頭文件,結(jié)果卻造成所有動(dòng)態(tài)庫(kù)的重新編譯。而這兩個(gè)選項(xiàng)就是為了解決這樣的問(wèn)題。
如果啟用了/Gm開(kāi)關(guān),編譯器在項(xiàng)目中的.idb文件中存儲(chǔ)了源文件和類定義之間的依賴關(guān)系。之后的編譯過(guò)程中使用.idb文件中的信息確定是否需要編譯某個(gè)源文件,哪怕是此源文件已經(jīng)包含了已修改的.h文件。
INCREMENTAL開(kāi)關(guān)默認(rèn)是開(kāi)啟的。使用增量鏈接生成的可執(zhí)行文件或者動(dòng)態(tài)鏈接庫(kù)會(huì)大于非增量鏈接的程序,因?yàn)橛写a和數(shù)據(jù)的填充。另外,增量鏈接的文件還包含跳轉(zhuǎn)trunk以處理函數(shù)重定位到新地址。
MSDN上明確指出:為確保終發(fā)布版本不包含填充或者trunk,請(qǐng)非增量鏈接程序。
_DEBUG
與
NDEBUG
這個(gè)話題放在本節(jié)的后,卻是重要的一個(gè)選項(xiàng)。這兩個(gè)是編譯器的預(yù)處理器定義,默認(rèn)情況下_DEBUG用于Debug版本,而NDEBUG用于Release版本。它們可以說(shuō)是重要的無(wú)以復(fù)加。因?yàn)椋琣ssert系列的斷言僅僅在_DEBUG下生效!
下面是assert.h文件中摘出來(lái)的:
#ifdef NDEBUG
#define assert(_Expression) ((void)0)
#else
#ifdef __cplusplus
extern "C" {
#endif
_CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);
#ifdef __cplusplus
}
#endif
#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
#endif /* NDEBUG */
可以看出在未定義_DEBUG時(shí),assert變成一條空語(yǔ)句不被執(zhí)行。
也就是說(shuō),我們現(xiàn)在所有發(fā)布的版本無(wú)法使用斷言機(jī)制進(jìn)行程序調(diào)試。
assert的作用就不用我細(xì)說(shuō)了吧。如果列位看官不知道assert為何物,那真得好好補(bǔ)補(bǔ)課了。
實(shí)際上,Debug和Release只是VC對(duì)編譯選項(xiàng)的預(yù)定義而已,如果我們?cè)敢猓覀兺耆梢园袲ebug和Release的行為完全顛倒過(guò)來(lái)。
當(dāng)然,我們也可以自己定義一組編譯選項(xiàng),然后命名為ABC,DEF等。當(dāng)然,在習(xí)慣上,我們?nèi)匀桓敢馐褂肰C已經(jīng)定義好的名稱。
既然Debug和Release僅僅是編譯選項(xiàng)的不同,那么為什么要區(qū)分Debug和Release版本呢?
Debug和Release,在我看來(lái)主要是針對(duì)其面向的目標(biāo)不同的而進(jìn)行區(qū)分的。
Debug通常稱為調(diào)試版本,通過(guò)一系列編譯選項(xiàng)的配合,編譯的結(jié)果通常包含調(diào)試信息,而且不做任何優(yōu)化,以為開(kāi)發(fā)人員提供強(qiáng)大的應(yīng)用程序調(diào)試能力。
而Release通常稱為發(fā)布版本,是為用戶使用的,一般客戶不允許在發(fā)布版本上進(jìn)行調(diào)試。所以不保存調(diào)試信息,同時(shí),它往往進(jìn)行了各種優(yōu)化,以期達(dá)到代碼小和速度優(yōu)。為用戶的使用提供便利。
下面僅就默認(rèn)的Debug和Release版本的選項(xiàng)進(jìn)行比較,詳細(xì)的編譯選項(xiàng)可以看MSDN的說(shuō)明。
我們將默認(rèn)的Debug和Release的選項(xiàng)設(shè)置進(jìn)行比較,過(guò)濾掉相同設(shè)置,主要的不同如下:
編譯選項(xiàng):/Od /D "_DEBUG" /Gm /RTC1 /MDd /Fo"Debug\\" /ZI
鏈接選項(xiàng):/OUT:"D:\MyProject\logging\Debug\OptionTest.dll" /INCREMENTAL
默認(rèn)的Release設(shè)置如下:
編譯選項(xiàng):/O2 /GL /D "NDEBUG" /FD /MD /Fo"Release\\" /Zi
鏈接選項(xiàng):/OUT:"D:\MyProject\logging\Release\OptionTest.dll" /INCREMENTAL:NO
MDd
與
MD
首先,Debug版本使用調(diào)試版本的運(yùn)行時(shí)庫(kù)(/MDd選項(xiàng)),Relase版本則使用的是發(fā)布版本的運(yùn)行時(shí)庫(kù)(vcrt.dll)。其區(qū)別主要在于運(yùn)行時(shí)的性能影響。調(diào)試版本的運(yùn)行時(shí)庫(kù)包含了調(diào)試信息,并采用了一些保護(hù)機(jī)制以幫助發(fā)現(xiàn)錯(cuò)誤,也因此,其性能不如發(fā)布版本。編譯器提供的Runtime Library很穩(wěn)定,不會(huì)造成Release版本錯(cuò)誤,倒是由于Debug版本的Runtime Library加強(qiáng)了對(duì)錯(cuò)誤的檢測(cè),如堆內(nèi)存分配檢查等,反而會(huì)報(bào)告錯(cuò)誤,應(yīng)當(dāng)指出,如果Debug有錯(cuò)誤,而Release版本正常,程序肯定是有Bug的,只是我們還沒(méi)有發(fā)現(xiàn)。
ZI
與
Zi
其次,/ZI選項(xiàng)與/Zi選項(xiàng)。通過(guò)使用/ZI選項(xiàng),可以在調(diào)試過(guò)程修改代碼而不需要重新編譯。這是個(gè)調(diào)試的好幫手,可如果我們使用Release版本,這將變得不可行。
Od
與
O2
/O2與/Od選項(xiàng):Od是關(guān)閉編譯器優(yōu)化,普遍用于Debug版本。而O2選項(xiàng)是創(chuàng)建快速代碼,這當(dāng)然是Release版本的不二選擇。
RTCx
選項(xiàng)
/RTCx選項(xiàng),這個(gè)選項(xiàng)比較強(qiáng),它可以讓編譯器插入動(dòng)態(tài)檢測(cè)代碼以幫助你檢測(cè)程序中的錯(cuò)誤。比如,它會(huì)將局部變量初始化為非零值。包括用0xCC初始化所有自動(dòng)變量,0xCD初始化堆中分配的內(nèi)存(即new的內(nèi)存),使用0xDD填充被釋放的內(nèi)存(即delete的內(nèi)存),0xFD初始化受保護(hù)的內(nèi)存(debug版在動(dòng)態(tài)分配內(nèi)存的前后加入保護(hù)內(nèi)存以防止越界訪問(wèn))。這樣做的好處是這些值都很大,一般不可能作為指針,考試,大提示作為數(shù)值也很少用到,而且這些值很容易辯認(rèn),因此有利于在Debug版本中發(fā)現(xiàn)Release版才會(huì)遇到的錯(cuò)誤。
另外,通過(guò)函數(shù)指針調(diào)用函數(shù)時(shí),會(huì)通過(guò)檢查棧指針驗(yàn)證函數(shù)調(diào)用的匹配性(防止原型不匹配)。
使用/RTCx選項(xiàng)會(huì)造成Debug版本出錯(cuò),而Release版本正常的現(xiàn)象,因?yàn)镽elease版中未初始化的變量是隨機(jī)的,很可能使指針指向了有效但是錯(cuò)誤的地址,從而掩蓋了錯(cuò)誤。
說(shuō)了這么多好處,這個(gè)編譯選項(xiàng)有個(gè)限制:那就是只能在/Od選項(xiàng)下使用。
Gm
,
INCREMENTAL or NO
編譯選項(xiàng)中的Gm和鏈接選項(xiàng)中的INCREMENTAL都只為一個(gè)目的,加快編譯速度。我們經(jīng)常遇上這樣的問(wèn)題,只修改了一個(gè)頭文件,結(jié)果卻造成所有動(dòng)態(tài)庫(kù)的重新編譯。而這兩個(gè)選項(xiàng)就是為了解決這樣的問(wèn)題。
如果啟用了/Gm開(kāi)關(guān),編譯器在項(xiàng)目中的.idb文件中存儲(chǔ)了源文件和類定義之間的依賴關(guān)系。之后的編譯過(guò)程中使用.idb文件中的信息確定是否需要編譯某個(gè)源文件,哪怕是此源文件已經(jīng)包含了已修改的.h文件。
INCREMENTAL開(kāi)關(guān)默認(rèn)是開(kāi)啟的。使用增量鏈接生成的可執(zhí)行文件或者動(dòng)態(tài)鏈接庫(kù)會(huì)大于非增量鏈接的程序,因?yàn)橛写a和數(shù)據(jù)的填充。另外,增量鏈接的文件還包含跳轉(zhuǎn)trunk以處理函數(shù)重定位到新地址。
MSDN上明確指出:為確保終發(fā)布版本不包含填充或者trunk,請(qǐng)非增量鏈接程序。
_DEBUG
與
NDEBUG
這個(gè)話題放在本節(jié)的后,卻是重要的一個(gè)選項(xiàng)。這兩個(gè)是編譯器的預(yù)處理器定義,默認(rèn)情況下_DEBUG用于Debug版本,而NDEBUG用于Release版本。它們可以說(shuō)是重要的無(wú)以復(fù)加。因?yàn)椋琣ssert系列的斷言僅僅在_DEBUG下生效!
下面是assert.h文件中摘出來(lái)的:
#ifdef NDEBUG
#define assert(_Expression) ((void)0)
#else
#ifdef __cplusplus
extern "C" {
#endif
_CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);
#ifdef __cplusplus
}
#endif
#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
#endif /* NDEBUG */
可以看出在未定義_DEBUG時(shí),assert變成一條空語(yǔ)句不被執(zhí)行。
也就是說(shuō),我們現(xiàn)在所有發(fā)布的版本無(wú)法使用斷言機(jī)制進(jìn)行程序調(diào)試。
assert的作用就不用我細(xì)說(shuō)了吧。如果列位看官不知道assert為何物,那真得好好補(bǔ)補(bǔ)課了。