Debug與Release版本的區(qū)別 Debug 和 Release并沒有本質(zhì)的區(qū)別,他們只是VC預(yù)定義提供的兩組編譯選項(xiàng)的集合,編譯器只是按照預(yù)定的選項(xiàng)行動。如果我們愿意,我們完全可以把Debug和Release的行為完全顛倒過來。當(dāng)然也可以提供其他的模式,例如自己定義一組編譯選項(xiàng),然后命名為MY_ABC等。習(xí)慣上,我們?nèi)匀桓敢馐褂肰C已經(jīng)定義好的名稱。
Debug版本包括調(diào)試信息,所以要比Release版本大很多(可能大數(shù)百K至數(shù)M)。至于是否需要DLL支持,主要看你采用的編譯選項(xiàng)。如果是基于ATL的,則Debug和Release版本對DLL的要求差不多。如果采用的編譯選項(xiàng)為使用MFC動態(tài)庫,則需要MFC42D.DLL等庫支持,而Release版本需要MFC42.DLL支持。Release不對源代碼進(jìn)行調(diào)試,不考慮MFC的診斷宏,使用的是 MFC Release庫,編譯時對應(yīng)用程序的速度進(jìn)行優(yōu)化,而Debug則正好相反,它允許對源代碼進(jìn)行調(diào)試,可以定義和使用MFC的 診斷宏,采用MFC Debug庫,對速度沒有優(yōu)化。
既然Debug和 Release僅僅是編譯選項(xiàng)的不同,那么為什么要區(qū)分Debug和Release版本呢?
Debug和Release,在我看來主要是針對其面向的目標(biāo)不同的而進(jìn)行區(qū)分的。Debug通常稱為調(diào)試版本,通過一系列編譯選項(xiàng)的配合,編譯的結(jié)果通常包含調(diào)試信息,而且不做任何優(yōu)化,以為開發(fā)人員提供強(qiáng)大的應(yīng)用程序調(diào)試能力。而Release通常稱為發(fā)布版本,是為用戶使用的,一般客戶不允許在發(fā)布版本上進(jìn)行調(diào)試。所以不保存調(diào)試信息,同時,它往往進(jìn)行了各種優(yōu)化,以期達(dá)到代碼最小和速度最優(yōu)。為用戶的使用提供便利。
下面僅就默認(rèn)的Debug和Release版本的選項(xiàng)進(jìn)行 比較,詳細(xì)的編譯選項(xiàng)可以看MSDN的說明。
我們將默認(rèn)的Debug和Release的選項(xiàng)設(shè)置進(jìn)行比較,過濾掉相同設(shè)置,主要的不同如下:
編譯選項(xiàng):/Od /D "_DEBUG" /Gm /RTC1 /MDd /Fo"Debug““" /ZI
鏈接選項(xiàng):/OUT:"D:“MyProject“logging“Debug“OptionTest.dll" /INCREMENTAL
Release設(shè)置:
編譯選項(xiàng):/O2 /GL /D "NDEBUG" /FD /MD /Fo"Release““" /Zi
鏈 接選項(xiàng):/OUT:"D:“MyProject“logging“Release“OptionTest.dll" /INCREMENTAL:NO
Debug 版本:
/MDd /MLd 或 /MTd 使用 Debug runtime library(調(diào)試版本的運(yùn)行 時刻函數(shù)庫)
/Od 關(guān)閉優(yōu)化開關(guān)
/D "_DEBUG" 相當(dāng)于 #define _DEBUG,打開編譯調(diào)試代碼 開關(guān)(主要針對assert函數(shù))
/ZI 創(chuàng)建 Edit and continue數(shù)據(jù)庫,在調(diào)試 過程中如果修改了源代碼不需重新編譯
/GZ 可以幫助捕獲內(nèi)存錯誤
/Gm 打開最小化重鏈接開關(guān),減少鏈接時 間
Release 版本:
/MD /ML 或 /MT 使用發(fā)布版本的運(yùn)行時刻函數(shù)庫
/O1 或 /O2 優(yōu) 化開關(guān),使程序最小或最快
/D "NDEBUG" 關(guān)閉條件編譯調(diào)試代碼開關(guān)(即不編譯assert函數(shù))
/GF 合并重 復(fù)的字符串,并將字符串常量放到只讀內(nèi)存,防止被修改
MDd與MD
首先,Debug版本使用調(diào)試版本的運(yùn)行時庫(/MDd選項(xiàng)),Relase版本則使用的是發(fā)布版本的運(yùn)行時庫(vcrt.dll)。其區(qū)別主要在于運(yùn)行時的性能影響。調(diào)試版本的運(yùn)行時庫包含了調(diào)試信息,并采用了一些保護(hù)機(jī)制以幫助發(fā)現(xiàn)錯誤,也因此,其性能不如發(fā)布版本。編譯器提供的RuntimeLibrary很穩(wěn)定,不會造成Release版本錯誤,倒是由于Debug版本的RuntimeLibrary加強(qiáng)了對錯誤的檢測,如堆內(nèi)存分配檢查等,反而會報告錯誤,應(yīng)當(dāng)指出,如果Debug有錯誤,而Release版本正常,程序肯定是有Bug的,只是我們還沒有發(fā)現(xiàn)。
ZI與Zi
其次,/ZI選項(xiàng)與/Zi選項(xiàng)。通過使用/ZI選項(xiàng),可以在調(diào)試過程修改代碼 而不需要重新編譯。這是個調(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)讓編譯器插入動態(tài)檢測代碼以幫助你檢測程序中的錯誤。比如,它會將局部變量初始化為非零值。包括用0xCC初始化所有自動變量,0xCD初始化堆中分配的內(nèi)存(即new的內(nèi)存),使用0xDD填充被釋放的內(nèi)存(即delete的內(nèi)存),0xFD初始化受保護(hù)的內(nèi)存(debug版在動態(tài)分配內(nèi)存的前后加入保護(hù)內(nèi)存以防止越界訪問)。這樣做的好處是這些值都很大,一般不可能作為指針,考試,大提示作為數(shù)值也很少用到,而且這些值很容易辯認(rèn),因此有利于在Debug版本中發(fā)現(xiàn)Release版才會遇到的錯誤。另外,通過函數(shù)指針調(diào)用函數(shù)時,會通過檢查棧指針驗(yàn)證函數(shù)調(diào)用的匹配性(防止原型不匹配)。使用/RTCx選項(xiàng)會造成Debug版本出錯,而Release版本正常的現(xiàn)象,因?yàn)镽elease版中未初始化的變量是隨機(jī)的,很可能使指針指向了有效但是錯誤的地址,從而掩蓋了錯誤。這個編譯選項(xiàng)只能在/Od選項(xiàng)下使用。
Gm,INCREMENTAL or NO
編譯選項(xiàng)中的Gm和鏈接選項(xiàng)中的INCREMENTAL都只為一個目的,加快編譯速度。我們經(jīng)常遇上這樣的問題,只修改了一個頭文件,結(jié)果卻造成所有動態(tài)庫的重新編譯。而這兩個選項(xiàng)就是為了解決這樣的問題。如果啟用了/Gm開關(guān),編譯器在項(xiàng)目中的.idb文件中存儲了源文件和類定義之間的依賴關(guān)系。之后的編譯過程中使用.idb文件中的信息確定是否需要編譯某個源文件,哪怕是此源文件已經(jīng)包含了已修改的.h文件。
INCREMENTAL開關(guān)默認(rèn)是開啟的。使用增量鏈接生 成的可執(zhí)行文件或者動態(tài)鏈接庫會大于非增量鏈接的程序,因?yàn)橛写a和數(shù)據(jù)的填充。另外,增量鏈接的文件還包含跳轉(zhuǎn)trunk以處理函數(shù)重定位到新地址。
MSDN 上明確指出:為確保最終發(fā)布版本不包含填充或者trunk,請非增量鏈接程序。
/GZ 選項(xiàng):做以下這些事
1.初 始化內(nèi)存和變量。包括用 0xCC 初始化所有自動變量,0xCD ( Cleared Data ) 初始化堆中分配的內(nèi)存(即動態(tài)分配 的內(nèi)存,例如 new ),0xDD ( Dead Data ) 填充已被釋放的堆內(nèi)存(例 如 delete ),0xFD( deFencde Data ) 初始化受保護(hù)的內(nèi)存(debug 版在動態(tài)分配內(nèi)存的前后加入保護(hù)內(nèi)存以防止越界訪問),其中括號中的詞是微軟建議的助記詞。這樣做的好處是這些值都很大,作為指針是不可能的(而且 32 位系統(tǒng)中指針很少是奇數(shù)值, 在有些系統(tǒng)中奇數(shù)的指針會產(chǎn)生運(yùn)行時錯誤),作為數(shù)值也很少遇到,而且這些值也很容易辨認(rèn),因此這很有利于在 Debug 版中發(fā) 現(xiàn) Release 版才會遇到的錯誤。要特別注意的是,很多人認(rèn)為編譯器會用 0 來初始化變量,這是錯誤的(而且這樣很不利于查找錯 誤)。
2. 通過函數(shù)指針調(diào)用函數(shù)時,會通過檢查棧指針驗(yàn)證函數(shù)調(diào)用的匹配性。(防止原形不匹配)
3. 函數(shù)返回前檢查 棧指針,確認(rèn)未被修改。(防止越界訪問和原形不匹配,與第二項(xiàng)合在一起可大致模擬幀指針省略 FPO )
通常 /GZ 選 項(xiàng)會造成 Debug 版出錯而 Release 版正常的現(xiàn)象,因?yàn)?nbsp; Release 版中未初始化的變量是隨機(jī)的,這有可能使指針指向一 個有效地址而掩蓋了非法訪問。
_DEBUG與NDEBUG
這是最重要的一個選項(xiàng)。這兩個是編譯器的預(yù)處理器定義,默認(rèn)情況下_DEBUG用于Debug版本,而NDEBUG用于Release版本。 它們可以說是重要的無以復(fù)加。因?yàn)?,assert系列的斷言僅僅在_DEBUG下生效!
下面是assert.h文件中摘出來的:
- #ifdef NDEBUG
- #define assert(_Expression) ((void)0)
- #else /* NDEBUG */
- #ifdef __cplusplus
- extern "C" {
- #endif /* __cplusplus */
- _CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
- #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
- #endif /* NDEBUG */
#ifdef NDEBUG#define assert(_Expression) ((void)0)#else /* NDEBUG */#ifdef __cplusplusextern "C" {#endif /* __cplusplus */_CRTIMP void __cdecl _wassert(__in_z const wchar_t * _Message, __in_z const wchar_t *_File, __in unsigned _Line);#ifdef __cplusplus}#endif /* __cplusplus */#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )#endif /* NDEBUG */
可以看出在未定義_DEBUG時,assert變成一條空語句不被執(zhí)行。
也就是說,我們現(xiàn)在所有發(fā)布的版本無法使 用斷言機(jī)制進(jìn)行程序調(diào)試。
相關(guān)經(jīng)驗(yàn):
1. 變量。
大家都知道,debug跟release在初始化變量時所做的操作是不同的,debug是將每個字節(jié)位都賦成0xcc,而release的賦值近似于隨機(jī)。如果你的程序中的某個變量沒被初始化就被引用,就很有可能出現(xiàn)異常:用作控制變量將導(dǎo)致流程導(dǎo)向不一致;用作數(shù)組下標(biāo)將會使程序崩潰;更加可能是造成其他變量的不準(zhǔn)確而引起其他的錯誤。所以在聲明變量后馬上對其初始化一個默認(rèn)的值是最簡單有效的辦法,否則項(xiàng)目大了你找都沒地方找。代碼存在錯誤在debug方式下可能會忽略而不被察覺到。debug方式下數(shù)組越界也大多不會出錯,在release中就暴露出來了,這個找起來就比較難了。
2. 自定義消息的消息參數(shù)。
MFC為我們提供了很好的消息機(jī)制,更增加了自定義消息,好處我就不用多說了。這也存在debug跟release的問題嗎?答案是肯定的。在自定義消息的函數(shù)體聲明時,時常會看到這樣的寫法:afx_msg LRESULT OnMessageOwn();Debug情況下一般不會有任何問題,而當(dāng)你在Release下且多線程或進(jìn)程間使用了消息傳遞時就會導(dǎo)致無效句柄之類的錯誤。導(dǎo)致這個錯誤直接原因是消息體的參數(shù)沒有添加,即應(yīng)該寫 成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam);3. release模式下不出錯,但debug模式下報錯。
這種情況下大多也是因?yàn)榇a書寫不正確引起的,查看MFC的源碼,可以發(fā)現(xiàn)好多ASSERT的語句(斷言),這個宏只是在debug模式下才有效,那么就清楚了,release版不報錯是忽略了錯誤而不是沒有錯誤,這可能存在很大的隱患,因?yàn)槭荄ebug模式下,比較方便調(diào)試,好好的檢查自己的代碼,再此就不多說了。
3. ASSERT, VERIFY, TRACE.......... 調(diào)試宏
這種情況很容易解釋。舉個例子:請?jiān)赩C下輸入ASSERT然后選中按F12跳到宏定義的地方,這里你就能夠發(fā)現(xiàn)Debug中ASSERT要執(zhí)行AfxAssertFailedLine,而Release下的宏定義卻為\"#define ASSERT(f)((void)0)\"。所以注意在這些調(diào)試宏的語句不要用程序相關(guān)變量如i++寫操作的語句。
VERIFY 是個例外,\"#define VERIFY(f) ((void)(f))\",即執(zhí)行。
哪些情況下Release版會出錯?
1. Runtime Library:鏈接哪種運(yùn)行時刻函數(shù)庫通常只對程序的性能產(chǎn)生影響。調(diào)試版本 的 Runtime Library 包含了調(diào)試信息,并采用了一些保護(hù)機(jī)制以幫助發(fā)現(xiàn)錯誤,因此性能不如發(fā)布版本。編譯器提供 的 Runtime Library 通常很穩(wěn)定,不會造成 Release 版錯誤;倒是由 于 Debug 的 Runtime Library 加強(qiáng)了對錯誤的檢測,如堆內(nèi)存分配,有時會出現(xiàn) Debug 有錯 但 Release 正常的現(xiàn)象。應(yīng)當(dāng)指出的是,如果 Debug 有錯,即使 Release 正常,程序肯定是有 Bug 的,只不 過可能是 Release 版的某次運(yùn)行沒有表現(xiàn)出來而已。
2. 優(yōu)化:這是造成錯誤的主要原因,因?yàn)殛P(guān)閉優(yōu)化時源程序基本上是直接翻譯的,而打開優(yōu)化后編譯器會作出一系列假設(shè)。這類錯誤主要有以下幾種:
(1) 幀指針 (Frame Pointer)省略(簡稱 FPO ):在函數(shù)調(diào)用過程中,所有調(diào)用信息(返回地址、參數(shù))以及自動變量都是放在棧中的。若函數(shù)的聲明與實(shí)現(xiàn)不同(參數(shù)、返回值、調(diào)用方式),就會產(chǎn)生錯誤,但 Debug 方式下,棧的訪問通過 EBP 寄存器保存的地址實(shí)現(xiàn),如果沒有發(fā)生數(shù)組越界之類的錯誤(或是越界“不多”),函數(shù)通常能正常執(zhí)行;Release 方式下,優(yōu)化會省略 EBP ?;分羔槪@樣通過一個全局 指針訪問棧就會造成返回地址錯誤是程序崩潰。C++ 的強(qiáng)類型特性能檢查出大多數(shù)這樣的錯誤,但如果用了強(qiáng)制類型轉(zhuǎn)換,就不行了。你可以 在 Release 版本中強(qiáng)制加入 /Oy- 編譯選項(xiàng)來關(guān)掉幀指針省略,以確定是否此類錯誤。此類錯誤通常有:
MFC消息響應(yīng)函數(shù)書寫錯誤。正確的應(yīng)為
afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam);
ON_MESSAGE 宏 包含強(qiáng)制類型轉(zhuǎn)換。防止這種錯誤的方法之一是重定義 ON_MESSAGE 宏,把下列代碼加到 stdafx.h 中 (在#include "afxwin.h"之后),函數(shù)原形錯誤時編譯會報錯
- #undef ON_MESSAGE
- #define ON_MESSAGE(message, memberFxn) \
- { message, 0, 0, 0, AfxSig_lwl, \
- (AFX_PMSG)(AFX_PMSGW)(static_cast< LRESULT (AFX_MSG_CALL \
- CWnd::*)(WPARAM, LPARAM) > (&memberFxn) },
#undef ON_MESSAGE#define ON_MESSAGE(message, memberFxn) { message, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (&memberFxn) },
(2) volatile 型變量:volatile 告訴編譯器該變量可能被程序之外的未知方式修改(如系統(tǒng)、其他進(jìn)程和線程)。優(yōu)化程序?yàn)榱耸钩绦蛐阅芴岣?,常把一些變量放在寄存器中(類似?nbsp;register 關(guān)鍵字),而其他進(jìn)程只能對該變量所在的內(nèi)存進(jìn)行修改,而寄存器中的值沒變。如果你的程序是多線程的,或者你發(fā)現(xiàn)某個變量的值與預(yù)期的不符而你確信已正確的設(shè)置了,則很可能遇到這樣的問題。這種錯誤有時會表現(xiàn)為程序在最快優(yōu)化出錯而最小優(yōu)化正常。把你認(rèn)為可疑的變量加上 volatile 試試。
(3) 變量優(yōu)化:優(yōu)化程序會根據(jù)變量的使用情況優(yōu)化變量。例如,函數(shù)中有一個未被使用的變量,在 Debug 版中它有可能掩蓋一個數(shù)組越界,而在 Release 版中,這個變量很可能被優(yōu)化調(diào),此時數(shù)組越界會破壞棧中有用的數(shù)據(jù)。當(dāng)然,實(shí)際的情況會比這復(fù)雜得多。與此有關(guān)的錯誤有:
非法訪問,包括數(shù)組越界、指針錯誤 等。例如
- void fn(void)
- {
- int i;
- i = 1;
- int a[4];
- {
- int j;
- j = 1;
- }
- a[-1] = 1;
- a[4] = 1;
- }
void fn(void){int i;i = 1;int a[4];{int j;j = 1;}a[-1] = 1; //當(dāng)然錯誤不會這么明顯,例如下標(biāo)是變量a[4] = 1;}
j 雖然在數(shù)組越界時已出了作用域,但其空間并未收回,因而 i 和 j 就會掩蓋越界。而 Release 版由于 i、j 并未其很大作用可能會被優(yōu)化掉,從而使棧被破壞。
3. _DEBUG 與 NDEBUG :當(dāng)定義了 _DEBUG 時,assert() 函數(shù)會被編譯, 而 NDEBUG 時不被編譯。除此之外,VC++中還有一系列斷言宏。這包括:
ANSI C 斷 言 void assert(int expression );
C Runtime Lib 斷 言 _ASSERT( booleanExpression );
_ASSERTE( booleanExpression );
MFC 斷 言 ASSERT( booleanExpression );
VERIFY( booleanExpression );
ASSERT_VALID( pObject );
ASSERT_KINDOF( classname, pobject );
ATL 斷 言 ATLASSERT( booleanExpression );
此外,TRACE() 宏的編譯也受 _DEBUG 控 制。
所有這些斷言都只在 Debug版中才被編譯,而在 Release 版中被忽略。唯一的例外 是 VERIFY() 。事實(shí)上,這些宏都是調(diào)用了 assert() 函數(shù),只不過附加了一些與庫有關(guān)的調(diào)試代碼。如果你在這些宏中加入了任何程序代碼,而不只是布爾表達(dá)式(例如賦值、能改變變量值的函數(shù)調(diào)用 等),那么 Release 版都不會執(zhí)行這些操作,從而造成錯誤。初學(xué)者很容 易犯這類錯誤,查找的方法也很簡單,因?yàn)檫@些宏都已在上面列出,只要利用 VC++ 的 Find in Files 功能在工程所有文件中 找到用這些宏的地方再一一檢查即可。另外,有些高手可能還會加入 #ifdef _DEBUG 之類的條件編譯,也要注意一下。
順便值 得一提的是 VERIFY() 宏,這個宏允許你將程序代碼放在布爾表達(dá)式里。這個宏通常用來檢查 Windows API 的返回值。有些人 可能為這個原因而濫用 VERIFY() ,事實(shí)上這是危險的,因?yàn)?nbsp; VERIFY() 違反了斷言的思想,不能使程序代碼和調(diào)試代碼完全分離, 最終可能會帶來很多麻煩。因此,專家們建議盡量少用這個宏。
一、"Debug是調(diào)試版本,包括的程序信息更多"
補(bǔ)充:只有DEBUG版的程序才能設(shè)置斷點(diǎn)、單步執(zhí)行、使用 TRACE/ASSERT等調(diào)試輸出語句。REALEASE不包含任何調(diào)試信息,所以體積小、運(yùn)行速度快。
I. 內(nèi)存分配問題
1. 變量未初始化。下面的程序在debug中運(yùn)行的很好。
- thing * search(thing * something)
- BOOL found;
- for(int i = 0; i < whatever.GetSize(); i++)
- {
- if(whatever[i]->field == something->field)
- {
- found = TRUE;
- break;
- }
- }
- if(found)
- return whatever[i];
- else
- return NULL;
thing * search(thing * something)BOOL found;for(int i = 0; i < whatever.GetSize(); i++){if(whatever[i]->field == something->field){ /* found it */found = TRUE;break;} /* found it */}if(found)return whatever[i];elsereturn NULL;
而在release中卻不行,因?yàn)閐ebug中會自動給變量初始化found=FALSE,而在release版中則不會。所以盡可能的給變量、類或結(jié)構(gòu) 初始化。
2. 數(shù)據(jù)溢出的問題
如:
- char buffer[10];
- int counter;
- lstrcpy(buffer, "abcdefghik");
char buffer[10];int counter;lstrcpy(buffer, "abcdefghik");
在debug版中buffer的NULL覆蓋了counter的高位,但是除非counter>16M,什么問題也沒有。但是在release版中,counter可能被放在寄存器中,這樣NULL就覆蓋了buffer下面的空間,可能就是函數(shù)的返回地址,這將導(dǎo)致 ACCESS ERROR。
3. DEBUG版和RELEASE版的內(nèi)存分配方式是不同的 。如果你在DEBUG版中申請 ele 為 6*sizeof(DWORD)=24bytes,實(shí)際上分配給你的是32bytes(debug版以32bytes為單位分配), 而在release版,分配給你的就是24bytes(release版以8bytes為單位),所以在debug版中如果你寫ele[6],可能不會有什么問題,而在release版中,就有ACCESS VIOLATE。
II. ASSERT和VERIFY
1. ASSERT在Release版本中是不會被編譯的。
ASSERT宏是這樣定義的
-
- #ifdef _DEBUG
- #define ASSERT(x) if( (x) == 0) report_assert_failure()
- #else
- #define ASSERT(x)
- #endif
#ifdef _DEBUG#define ASSERT(x) if( (x) == 0) report_assert_failure()#else#define ASSERT(x)#endif
實(shí)際上復(fù)雜一些,但無關(guān)緊要。假如你在這些語句中加了程序中必須要有的代碼
比如
-
- ASSERT(pNewObj = new CMyClass);
-
- pNewObj->MyFunction();
ASSERT(pNewObj = new CMyClass);pNewObj->MyFunction();
這種時候Release版本中的pNewObj不會分配到空間
所以執(zhí)行到下一個語句的時候程序會報該程序執(zhí)行了非法操作的錯誤。這時可以用VERIFY :
-
- #ifdef _DEBUG
- #define VERIFY(x) if( (x) == 0) report_assert_failure()
- #else
- #define VERIFY(x) (x)
- #endif
#ifdef _DEBUG#define VERIFY(x) if( (x) == 0) report_assert_failure()#else#define VERIFY(x) (x)#endif
這樣的話,代碼在release版中就可以執(zhí)行了。
III. 參數(shù)問題:
自定義消息的處理函數(shù),必須定義如下:
afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);
返回值必須是HRESULT型,否則Debug會過,而Release出錯
IV. 內(nèi)存分配
保證數(shù)據(jù)創(chuàng)建和清除的統(tǒng)一性:如果一個DLL提供一個能夠創(chuàng)建數(shù)據(jù)的函數(shù),那么這個DLL同時應(yīng)該提供一個函數(shù)銷毀這些數(shù)據(jù)。數(shù)據(jù)的創(chuàng)建和清除應(yīng)該在同一 個層次上。
V. DLL的災(zāi)難
人們將不同版本DLL混合造成的不一致性形象的稱為 “動態(tài)連接庫的地獄“(DLL Hell) ,甚至微軟自己也這么說
http://msdn.microsoft.com/library/techart/dlldanger1.htm)。
如果你的程序使用你自己的DLL時請注意:
1. 不能將debug和release版的DLL混合在一起使用。debug都是debug版,release版都是release版。
解決辦法是將debug和release的程序分別放在主程序的debug和release目錄下
2. 千萬不要以為靜態(tài)連接庫會解決問題,那只會使情況更糟糕。
VI. RELEASE版中的調(diào)試 :
1. 將ASSERT() 改為 VERIFY() 。找出定義在"#ifdef _DEBUG"中的代碼,如果在RELEASE版本中需要這些代碼請將他們移到定義外。查找TRACE(...)中代碼,因?yàn)檫@些代碼在RELEASE中也不被編譯。 請認(rèn)真檢查那些在RELEASE中需要的代碼是否并沒有被便宜。
2. 變量的初始化所帶來的不同,在不同的系統(tǒng),或是在DEBUG/RELEASE版本間都存在這樣的差異,所以請對變量進(jìn)行初始化。
3. 是否在編譯時已經(jīng)有了警告?請將警告級別設(shè)置為3或4,然后保證在編譯時沒有警告出現(xiàn).
VII. 將Project Settings" 中 "C++/C " 項(xiàng)目下優(yōu)化選項(xiàng)改為Disbale(Debug)。編譯器的優(yōu)化可能導(dǎo)致許多意想不到的錯誤,請參http://www.pgh.net/~newcomer/debug_release.htm
1. 此外對RELEASE版本的軟件也可以進(jìn)行調(diào)試,請做如下改動:
在"Project Settings"中"C++/C"項(xiàng)目下設(shè)置"category"為"General"并且將"Debug Info"設(shè)置為"Program Database"。
在"Link"項(xiàng)目下選中"Generate Debug Info"檢查框。
"Rebuild All"
如此做法會產(chǎn)生的一些限制:
無法獲得在MFC DLL中的變量的值。
必須對該軟件所使用的所有DLL工程都進(jìn)行改動。
另:
1. MS BUG:MS的一份技術(shù)文檔中表明,在VC5中對于DLL的"Maximize Speed"優(yōu)化選項(xiàng) 并未被完全支持,因此這將會引起內(nèi)存錯誤并導(dǎo)致程序崩潰。
2. http://www.sysinternals.com/有一個程序DebugView,用來捕捉OutputDebugString的輸出,運(yùn)行起來后(估計(jì)是自設(shè)為systemdebugger)就可以觀看所有程序的OutputDebugString的輸出。此后,你可以脫離VC來運(yùn)行你的程序并觀看調(diào)試信息。
3. 有一個叫Gimpel Lint的靜態(tài)代碼檢查工具,據(jù)說比較好用http://www.gimpel.com/ 不過要化$的。
Debug與Release不同的問題在剛開始編寫代碼時會經(jīng)常發(fā)生,99%是因?yàn)槟愕拇a書寫錯誤而導(dǎo)致的,所以不要動不動就說系統(tǒng)問題或編譯器問題,努力找找自己的原因才是根本。我從前就常常遇到這情況,經(jīng)歷過一次次的教訓(xùn)后我就開始注意了,現(xiàn)在我所寫過的代碼我已經(jīng)好久沒遇到這種問題了。下面是幾個避免的方面,即使沒有這種問題也應(yīng)注意一下:
1. 注意變量的初始化,尤其是指針變量,數(shù)組變量的初始化(很大的情況下另作考 慮了)。
2. 自定義消息及其他聲明的標(biāo)準(zhǔn)寫法
3. 使用調(diào)試宏時使用后最好注釋掉
4. 盡量使用 try - catch(...)
5. 盡量使用模塊,不但表達(dá)清楚而且方便調(diào)試。