1. 程式人生 > >Xcode release與debug編譯方式的區別

Xcode release與debug編譯方式的區別

Debug和Release僅僅是編譯選項的不同,那麼為什麼要區分Debug和Release版本呢?

Debug和Release,主要是針對其面向的目標不同的而進行區分的。

Debug通常稱為除錯版本,通過一系列編譯選項的配合,編譯的結果通常包含除錯資訊,而且不做任何優化,以為開發人員提供強大的應用程式除錯能力。

而Release通常稱為釋出版本,是為使用者使用的,一般客戶不允許在釋出版本上進行除錯。所以不儲存除錯資訊,同時,它往往進行了各種優化,以期達到程式碼最小和速度最優。為使用者的使用提供便利。

下面僅就預設的Debug和Release版本的選項進行比較,詳細的編譯選項可以看MSDN的說明。

我們將預設的Debug和Release的選項設定進行比較,過濾掉相同設定,主要的不同如下:

編譯選項:/Od /D "_DEBUG" /Gm /RTC1 /MDd /Fo"Debug\\" /ZI

連結選項:/OUT:"D:\MyProject\logging\Debug\OptionTest.dll" /INCREMENTAL

預設的Release設定如下:

編譯選項:/O2 /GL /D "NDEBUG" /FD /MD /Fo"Release\\" /Zi

連結選項:/OUT:"D:\MyProject\logging\Release\OptionTest.dll" /INCREMENTAL:NO

MDd與MD
首先,Debug版本使用除錯版本的執行時庫(/MDd選項),Relase版本則使用的是釋出版本的執行時庫(vcrt.dll)。其區別主要在於執行時的效能影響。除錯版本的執行時庫包含了除錯資訊,並採用了一些保護機制以幫助發現錯誤,也因此,其效能不如釋出版本。編譯器提供的Runtime Library很穩定,不會造成Release版本錯誤,倒是由於Debug版本的Runtime Library加強了對錯誤的檢測,如堆記憶體分配檢查等,反而會報告錯誤,應當指出,如果Debug有錯誤,而Release版本正常,程式肯定是有Bug

的,只是我們還沒有發現。

ZI與Zi
其次,/ZI選項與/Zi選項。通過使用/ZI選項,可以在除錯過程修改程式碼而不需要重新編譯。這是個除錯的好幫手,可如果我們使用Release版本,這將變得不可行。

Od與O2
/O2與/Od選項:Od是關閉編譯器優化,普遍用於Debug版本。而O2選項是建立最快速程式碼,這當然是Release版本的不二選擇。

RTCx選項
/RTCx選項,這個選項比較強,它可以讓編譯器插入動態檢測程式碼以幫助你檢測程式中的錯誤。比如,它會將區域性變數初始化為非零值。包括用0xCC初始化所有自動變數,0xCD初始化堆中分配的記憶體(即new的記憶體),使用0xDD填充被釋放的記憶體(即delete的記憶體),0xFD初始化受保護的記憶體(debug版在動態分配記憶體的前後加入保護記憶體以防止越界訪問)。這樣做的好處是這些值都很大,一般不可能作為指標,作為數值也很少用到,而且這些值很容易辯認,因此有利於在Debug版本中發現Release版才會遇到的錯誤。

另外,通過函式指標呼叫函式時,會通過檢查棧指標驗證函式呼叫的匹配性(防止原型不匹配)。

使用/RTCx選項會造成Debug版本出錯,而Release版本正常的現象,因為Release版中未初始化的變數是隨機的,很可能使指標指向了有效但是錯誤的地址,從而掩蓋了錯誤。

說了這麼多好處,這個編譯選項有個限制:那就是隻能在/Od選項下使用。

Gm,INCREMENTAL or NO
編譯選項中的Gm和連結選項中的INCREMENTAL都只為一個目的,加快編譯速度。我們經常遇上這樣的問題,只修改了一個頭檔案,結果卻造成所有動態庫的重新編譯。而這兩個選項就是為了解決這樣的問題。

如果啟用了/Gm開關,編譯器在專案中的.idb檔案中儲存了原始檔和類定義之間的依賴關係。之後的編譯過程中使用.idb檔案中的資訊確定是否需要編譯某個原始檔,哪怕是此原始檔已經包含了已修改的.h檔案。

INCREMENTAL開關預設是開啟的。使用增量連結生成的可執行檔案或者動態連結庫會大於非增量連結的程式,因為有程式碼和資料的填充。另外,增量連結的檔案還包含跳轉trunk以處理函式重定位到新地址。

MSDN上明確指出:為確保最終釋出版本不包含填充或者trunk,請非增量連結程式。

_DEBUG與NDEBUG
這個話題放在本節的最後,卻是最重要的一個選項。這兩個是編譯器的前處理器定義,預設情況下_DEBUG用於Debug版本,而NDEBUG用於Release版本。它們可以說是重要的無以復加。因為,assert系列的斷言僅僅在_DEBUG下生效!

下面是assert.h檔案中摘出來的:

#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時,assert變成一條空語句不被執行。

也就是說,我們現在所有釋出的版本無法使用斷言機制進行程式除錯