最近寫代碼過程中,發現 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 並沒有本質的界限,他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動。
相關經驗: 轉自http://dev.csdn.net/article/17/17068.shtm
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語句是一樣地參考資料:http://it.china-b.com/ejks/c/20090521/5088_1.htmlhttp://home.putclub.com/space.php?uid=738315&do=blog&id=39809