VC++中Debug除錯版本和Release發行版本的區別
在寫程式碼過程中,發現 Debug 下執行正常,Release 下就會出現問題,百思不得其解,而Release 下又無法進行除錯,於是只能採用printf方式逐步定位到問題所在處,才發現原來是給定的一個數組未初始化,導致後面處理異常。網上查找了些資料,在這 羅列彙總下,做為備忘~
一、Debug 和 Release 的區別
Debug 通常稱為除錯版本,它包含除錯資訊,並且不作任何優化,便於程式設計師除錯程式。Release 稱為釋出版本,它往往是進行了各種優化,使得程式在程式碼大小和執行速度上都是最優的,以便使用者很好地使用。
Debug 和 Release 的真正區別,在於一組編譯選項。
Debug 版本
引數 含義
/MDd /MLd 或 /MTd 使用 Debug runtime library(除錯版本的執行時刻函式庫)
/Od 關閉優化開關
/D "_DEBUG" 相當於 #define _DEBUG,開啟編譯除錯程式碼開關(主要針對assert函式)
/ZI
建立 Edit and continue(編輯繼續)資料庫,這樣在除錯過程中如果修改了原始碼不需重新編譯
GZ 可以幫助捕獲記憶體錯誤
Release 版本 引數含義
/MD /ML 或 /MT 使用釋出版本的執行時刻函式庫
/O1 或 /O2 優化開關,使程式最小或最快
/D "NDEBUG" 關閉條件編譯除錯程式碼開關(即不編譯assert函式)
/GF 合併重複的字串,並將字串常量放到只讀記憶體,防止被修改
Debug 和 Release 並沒有本質的界限,他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動。
相關經驗: 轉自
1. 變數。
大家都知道,debug跟release在初始化變數時所做的操作是不同的,debug是將每個位元組位都賦成0xcc(注1),而release的賦值近 似於隨機(我想是直接從記憶體中分配的,沒有初始化過)。這樣就明確了,如果你的程式中的某個變數沒被初始化就被引用,就很有可能出現異常:用作控制變數將 導致流程導向不一致;用作陣列下標將會使程式崩潰;更加可能是造成其他變數的不準確而引起其他的錯誤。所以在宣告變數後馬上對其初始化一個預設的值是最簡 單有效的辦法,否則專案大了你找都沒地方找。程式碼存在錯誤在debug方式下可能會忽略而不被察覺到,如debug方式下陣列越界也大多不會出錯,在 release中就暴露出來了,這個找起來就比較難了:( 還是自己多加註意吧
呵呵,就是我犯的問題~~
2. 自定義訊息的訊息引數。
MFC為我們提供了很好的訊息機制,更增加了自定義訊息,好處我就不用多說了。這也存在debug跟release的問題嗎?答案是肯定的。在自定義訊息 的函式體宣告時,時常會看到這樣的寫法:afx_msg LRESULT OnMessageOwn(); Debug情況下一般不會有任何問題,而當你在Release下且多執行緒或程序間使用了訊息傳遞時就會導致無效控制代碼之類的錯誤。導致這個錯誤直接原因是消 息體的引數沒有新增,即應該寫成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); (注2
3. release模式下不出錯,但debug模式下報錯。
這種情況下大多也是因為程式碼書寫不正確引起的,檢視MFC的原始碼,可以發現好多ASSERT的語句(斷言),這個巨集只是在debug模式下才有效,那麼就 清楚了,release版不報錯是忽略了錯誤而不是沒有錯誤,這可能存在很大的隱患,因為是Debug模式下,比較方便除錯,好好的檢查自己的程式碼,再此 就不多說了。
4. ASSERT, VERIFY, TRACE..........除錯巨集
這種情況很容易解釋。舉個例子:請在VC下輸入ASSERT然後選中按F12跳到巨集定義的地方,這裡你就能夠發現Debug中ASSERT要執行 AfxAssertFailedLine,而Release下的巨集定義卻為"#define ASSERT(f) ((void)0)"。所以注意在這些除錯巨集的語句不要用程式相關變數如i++寫操作的語句。VERIFY是個例外,"#define VERIFY(f) ((void)(f))",即執行,這裡的作用就不多追究了,有興趣可自己研究:)。
總結:
Debug與Release不同的問題在剛開始編寫程式碼時會經常發生,99%是因為你的程式碼書寫錯誤而導致的,所以不要動不動就說系統問題或編譯器問題, 努力找找自己的原因才是根本。我從前就常常遇到這情況,經歷過一次次的教訓後我就開始注意了,現在我所寫過的程式碼我已經好久沒遇到這種問題了。下面是幾個 避免的方面,即使沒有這種問題也應注意一下:
1. 注意變數的初始化,尤其是指標變數,陣列變數的初始化(很大的情況下另作考慮了)。
2. 自定義訊息及其他宣告的標準寫法
3. 使用除錯巨集時使用後最好註釋掉
4. 儘量使用try - catch(...)
5. 儘量使用模組,不但表達清楚而且方便除錯。
注1:
debug版初始化成0xcc是因為0xcc在x86下是一條int 3單步中斷指令,這樣程式如果跑飛了遇到0xcc就會停下來,這和微控制器程式設計時一般將沒用的程式碼空間填入jmp 0000語句是一樣地轉貼於:計算機二級考試_考試大【責編:drfcy 糾錯】
[VC]DEBUG和RELEASE2007年08月26日 星期日 下午 04:33
I. 記憶體分配問題
1. 變數未初始化。下面的程式在debug中執行的很好。
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];
else
return NULL;
而在release中卻不行,因為debug中會自動給變數初始化found=FALSE,而在release版中則不會。所以儘可能的給變數、類或結構初始化。
2. 資料溢位的問題
如:char buffer[10];
int counter;
lstrcpy(buffer, "abcdefghik");
在debug版中buffer的NULL覆蓋了counter的高位,但是除非counter>16M,什麼問題也沒有。但是在release版 中,counter可能被放在暫存器中,這樣NULL就覆蓋了buffer下面的空間,可能就是函式的返回地址,這將導致ACCESS ERROR。
3. DEBUG版和RELEASE版的記憶體分配方式是不同的
如果你在DEBUG版中申請 ele 為 6*sizeof(DWORD)=24bytes,實際上分配給你的是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
實際上覆雜一些,但無關緊要。假如你在這些語句中加了程式中必須要有的程式碼
比如
ASSERT(pNewObj = new CMyClass);
pNewObj->MyFunction();
這種時候Release版本中的pNewObj不會分配到空間
所以執行到下一個語句的時候程式會報該程式執行了非法操作的錯誤。這時可以用VERIFY :
#ifdef _DEBUG
#define VERIFY(x) if( (x) == 0) report_assert_failure()
#else
#define VERIFY(x) (x)
#endif
這樣的話,程式碼在release版中就可以執行了。
III. 引數問題:
自定義訊息的處理函式,必須定義如下:
afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);
返回值必須是HRESULT型,否則Debug會過,而Release出錯
IV. 記憶體分配
保證資料建立和清除的統一性:如果一個DLL提供一個能夠建立資料的函式,那麼這個DLL同時應該提供一個函式銷燬這些資料。資料的建立和清除應該在同一個層次上。
V. DLL的災難
人們將不同版本DLL混合造成的不一致性形象的稱為 “動態連線庫的地獄“(DLL Hell) ,甚至微軟自己也這麼說http://msdn.microsoft.com/library/techart/dlldanger1.htm)。
如果你的程式使用你自己的DLL時請注意:
1. 不能將debug和release版的DLL混合在一起使用。debug都是debug版,release版都是release版。
解決辦法是將debug和release的程式分別放在主程式的debug和release目錄下
2. 千萬不要以為靜態連線庫會解決問題,那隻會使情況更糟糕。
VI. RELEASE板中的除錯 :
1. 將ASSERT() 改為 VERIFY() 。找出定義在"#ifdef _DEBUG"中的程式碼,如果在RELEASE版本中需要這些程式碼請將他們移到定義外。查詢TRACE(...)中程式碼,因為這些程式碼在RELEASE中 也不被編譯。 請認真檢查那些在RELEASE中需要的程式碼是否並沒有被便宜。
2. 變數的初始化所帶來的不同,在不同的系統,或是在DEBUG/RELEASE版本間都存在這樣的差異,所以請對變數進行初始化。
3. 是否在編譯時已經有了警告?請將警告級別設定為3或4,然後保證在編譯時沒有警告出現.
VII. 將Project Settings" 中 "C++/C " 專案下優化選項改為Disbale(Debug)。
編譯器的優化可能導致許多意想不到的錯誤,請參http://www.pgh.net/~newcomer/debug_release.htm
1. 此外對RELEASE版本的軟體也可以進行除錯,請做如下改動:
在"Project Settings" 中 "C++/C " 專案下設定 "category" 為 "General" 並且將"Debug Info"設定為 "Program Database"。
在"Link"專案下選中"Generate Debug Info"檢查框。
"Rebuild All"
如此做法會產生的一些限制:
無法獲得在MFC DLL中的變數的值。
必須對該軟體所使用的所有DLL工程都進行改動。
另:
MS BUG:MS的一份技術文件中表明,在VC5中對於DLL的"Maximize Speed"優化選項並未被完全支援,因此這將會引起記憶體錯誤並導致程式崩潰。