引言
如果在您的開發過程中遇到了常見的錯誤,或許您的 Release 版本不能正常運行而 Debug 版本運行無誤,那麼我推薦您閱讀本文:因爲並非如您想象的那樣, Release 版本可以保證您的應用程序可以象 Debug 版本一樣運行。
如果您在開發階段完成之後或者在開發進行一段時間之內從來沒有進行過 Release 版本測試,然而當您測試的時候卻發現問題,那麼請看我們的調試規則 1:
規則 1 : 經常性對開發軟件進行 Debug 和 Release 版本的常規測試 .
測試 Release 版本的時間間隔越長,排除問題的難度越大,至少對 Release 版本進行每週 1 次的測試,可以使您在緊湊的開發週期內節省潛在的排故時間 .
不要隨意刪除 Release 版本需要的代碼
這點看起來似乎再明顯不過,但卻是開發人員無意中經常犯的錯誤,原因在於編譯器編譯 Release 版本時候會主動排除在代碼中存在的宏,例如 ASSERT 和 TRACE 在 Release 版本會自動排除,這樣導致的問題是您在這些宏當中運行的代碼也被隨之刪除,這是非常危險的事情 J , 例如:
ASSERT ( m_ImageList . Create ( MAKEINTRESOURCE ( IDB_IMAGES ), 16, 1, RGB (255,255,255)));
這樣的代碼在 Debug 模式不會出錯,圖像列表也自動創建了,然而在 Release 版本呢?後繼使用 m_ImageList 對象只會造成程序的 Crash! ,因此 ASSERT 宏中儘量使用邏輯運算符作爲驗證。
規則 2 : 不要將代碼放置在僅在某種編譯選項中執行的地方,對於使用 _DEBUG 等編譯選項宏內部的代碼必須不影響整個程序的使用 .
規則 3 : 不要使用規則 2 作爲評判標準來刪除 ASSERT 宏, ASSERT 宏是個有用的工具,但容易使用錯誤 .
使 Debug 編譯模式接近 Release 模式
如果您的 Release 版本存在的問題是由代碼被編譯器自動排除造成的,那麼通過這個方法您的問題可能會重現 .
一些問題的產生可能是由於不同編譯選項之間預定義符號造成的,因此您可以更改編譯模式下的預定義符號,從而使您的 Debug 模式接近 Release 模式,觀察錯誤是否產生,更改編譯預定義符號方法如下 :
- Alt-F7 打開項目設置,在 C++/C 頁面,選擇 "General" 類別,更改 "_DEBUG " 符號爲 "NDEBUG ".
- 在 C++/C 頁面 , 選擇 "Preprocessor" 類別,添加預定義符號 "_DEBUG " 到 "Undefined Symbols" 欄 .
- 使用 "Rebuild All" 重新編譯
如果通過上面設置,您在 Release 編譯模式下面的問題在 Debug 模式下重現,那麼請您依據以下步驟對您的代碼進行修改 :
- 查找 ASSERT 排除其中的所有重要執行語句,或者將 ASSERT 修改爲 VERIFY .
- 檢查 "#ifdef _DEBUG " 內所有代碼,排除 Release 模式使用的代碼 .
- 查找 TRACE 排除其中的所有重要執行語句 . TRACE 和 ASSERT 一樣,僅在 Debug 模式下編譯 .
如果通過上面修改更正了您在 Debug 模式下的問題,那麼您可以重新編譯 Release 模式,非常有可能您可以解決先前存在的問題! .
錯誤的假定造成編譯模式錯誤
您是否經常性的假定您的變量或者對象被初試化成某個指定的值 ( 可能 0) ?您是否假定你所有關聯到的資源在應用程序中都存在?這些也是 Debug 和 Release 模式下不同問題產生的原因 .
規則 4 : 除非您在代碼中對變量進行初始化,否則不能作出如上假定 . 包括全局變量,自動變量,申請對象和 new 對象 .
這種情況還常常發生在內存順序的問題,記得原來使用結構體的時候爲了使用方便,比較兩個結構體對象使用 memcmp ,在 Debug 版本工作正常,而 Release 版本計算出錯誤的解,看來的確不能進行錯誤的假定!
規則 5 : 確保刪除資源的所有引用都被刪除,例如 resource.h 中的定義 .
軟件開發中,不同編譯版本對變量和內存的初始化是不同的 . 如果您假定變量初始化爲 0, 那麼在 Win9x 系統的 Release 模式下,會出現異常現象。因此對所有變量,內存顯式清 0 是較爲安全的做法 .
如果您引用了已經被刪除的資源,您的 Debug 版本可以正常工作,但是 Release 版本可能會 crash.
您是否相信編譯器 ?
編譯器警告級別和編譯噪音有着相當大的關係 .
通過提高編譯器警告級別可增加程序隱藏問題暴露的機會 . 通常設置警告級別在 "Level 3" 或者 "Level 4". 編譯並解決所有警告,這是發佈 Release 版本應用程序的一個很好的建議 . 這能暴露會使您的應用程序出現問題的很多初始化問題和其它潛在的錯誤 .
規則 6 : 開始項目之前先將編譯警告級別設置在 "Level 3" 或者 "Level 4" ,登記代碼之前確保消滅所有警告! .
總結報告
編譯模式下的調試
曾經不止一次的聽到一些 VC 開發者說 Release 模式下面不能進行調試,幸運的是:通過相應設置,可以在 Release 模式進行調試,因此那只不過是一個以訛傳訛的荒謬說法而已 .
規則 7 : 當前面所有的方法都無效的時候,在 Release 模式下面進行調試 .
Release 模式可以進行調試,第一步是打開符號表 :
- Alt-F7 打開項目設置,在 C++/C 頁面,選擇 "General" 類,修改 Debug Info setting 爲 "Program Database".
- 在 "Link" 頁面,選擇 "Generate Debug Info".
- "Rebuild All"
這些設置將允許您在 Release 模式下保留符號表,您也可以同時考慮以下設置 :
- 調試 Release 版本應用程序,您可以關閉優化選項 .
- 如果在 Release 模式下面不能設置斷點,添加指令 "__asm {int 3}" 可以是您的應用程序在改行停止 ( 確定在發佈應用程序時候排除這些代碼 ).
在 Release 模式進行調試的幾個限制 .
- 最大的問題在於您不能跟蹤到 MFC 函數內部,原因在於 Release 版本的 MFC 動態鏈接庫不包含調試信息和符號表 .
- 同上,想要調試調用的 dll ,您必須給它們全部加上調試信息和符號表 .
編譯器生成了錯誤的代碼 ?
或許有的時候您會發現 VC++ 編譯器生成了 ’ 問題代碼 ’ ,然而坦率的講,人們通常抱怨的太早 . 您可以在 Release 模式下面關閉優化選項來進行測試 .
如果這個操作解決了您的問題,或許您的編碼習慣存在問題 . 信不信由你 , 極其可能在您的編碼中存在模棱兩可的求解或者看起來似乎正確,某些條件下也是正確的情況 . 舉個例子,下面的代碼在 Debug 模式似乎一切 ’ 正常 ’ ,而在 Release 模式下面卻會出錯!
#include <stdio.h>
int
* func1()
{
int
retval =
5
;
return
&retval;
}
int
main(
int
argc,
char
* argv[])
{
printf(
"%d/n"
, *func1());
return
0
;
}
我相信大多數程序員尤其是初學者容易遇到此類情況的 .
規則 8 : 如果關閉 Release 模式的優化選項可以使您的應用程序運行正常,而打開優化選項則出現問題的化,原因多半在於您的不良編碼習慣造成的 . 這意味着必須仔細檢查您的代碼,清理出那些錯誤的假設,懸空指針等等 . 等同的這告訴您,在 Debug 模式和關閉優化選項的 Release 模式下您的應用程序工作正常全是因爲系統隱含的運氣,您必須着手更正存在隱患的代碼,否則在日後可能會造成巨大的損失 .
規則 9 : 如果您已經徹底檢查了您的代碼,並且沒有發現問題,那麼您最好逐個打開優化選項將產生錯誤的原因限制在某個範圍之內 .
BTW- 以上問題代碼由 C++ 編譯器自動檢出 . 如果您已經遵循 規則 6 您或許在前面環節中已經解決了這些問題 .
憑我的開發經驗,編譯器極少會產生錯誤的代碼(當然要注意接口程序邊界對齊的問題) . 通常在使用模板類時候 VC6 編譯器或許會產生斷言 ASSERT 錯誤,這種情況您只需更新補丁即可解決 .
最後的思考
在日常編碼中只需稍微增加一點嚴格的檢測,便能有效的避免新的 Debug -v- Release 模式問題的產生,以下是我的一些經驗 .
1. 取出 (check out) 需要修改的代碼 .
2. 修改代碼,排除所有警告,編譯 Debug 和 Release 版本 .
3. 詳細測試新代碼,即單步調試新代碼段之後進入工作代碼,確保代碼無誤 .
4. 更正所有問題 .
5. 確認無誤之後將新代碼登記入庫 (check in).
6. 對登記入庫的代碼進行全新的編譯,確保新登記代碼與其它代碼融合 .
7. 重新詳細測試代碼 .
8. 更正新問題 ( 或許可以發現登記入庫代碼存在的問題 )
嚴格按照以上步驟,您在設計開發過程中即可解決大量問題,避免在最後發佈應用程序時候產生新的難以定位的問題 .
後記
本文是在我的開發歷程中遇到 Release 版本應用程序發佈,產生錯誤的時候苦苦求索得到的一些經驗,原文來自於 codeproject ,經過本人潤色,改寫成爲適合國內開發者的文章,希望能對大家有用,謝謝!
原帖:http://blog.csdn.net/chinacodec/archive/2009/01/26/3853473.aspx