Debug版本和Release版本之所以有區別,就是在於各自的編譯項不同。不同的編譯項組合產生不同的代碼,Debug版本的編譯項屏蔽了編譯器的優化,增加了調試信息的輸出。也就是說,可以通過修改編譯項的組合,使得優化過的版本也包含調試信息。於是調試Release版本成爲了可能。那就先說說調試方面Debug做的工作。
提起Vc6的調試,不能不提的就是它的PDB(Program Database)。編譯項加上 /Zi 或者 /ZI 都能生成PDB文件,其中Debug版本默認編譯項使用的是 /ZI 選項,因爲 /ZI 選項支持Edit And Continue特性。PDB文件的內容非常豐富,包括公共函數,公共變量的名字信息和位置信息;靜態函數、靜態變量、局部變量、函數參數等非公共的信息的名字和位置;類型信息自然是不能少的;還有源文件和代碼行的信息等。文件信息是絕對路徑,所以把程序和整個DEBUG目錄一塊兒複製到另一個地方時,需要重新編譯才能調試,因爲路徑變了~ 呵呵~ Release版本也可以生成PDB文件,加上編譯項 /Zi 就可以了。PDB文件可以幫我們簡單而快捷的定位內存訪問異常導致程序退出的錯誤。
接下來是內存管理部分。這一部分的區別有些經驗的朋友應該都比較清楚。Debug模式下,我們看到棧(stack)上分配的內存都被初始化成了0xcc,而堆(heap)上分配的內存都被初始化成了0xcd(微軟稱之爲_bCleanLandFill),並且在分配的內存的兩頭各有四個字節的0xFD作爲邊界(微軟稱之爲_bNoMansLandFill),方便檢測內存越界。這個邊界是用來方便程序員查錯的,但是同時也導致了和Release版本下的行爲有可能出現差異。
還有個小差異就是volatile類型的處理。Debug版本下,所有的變量都是volatile類型。而Release版本下由於優化的關係,如果多線程環境下該聲明爲volatile的不聲明,那是會出問題滴。本來想寫段代碼來測試這種差異,但是失敗了。
#include <windows.h>
BOOL g_bExit = FALSE; //不加volatile關鍵字
DWORD WINAPI Worker(LPVOID)
{
while(!g_bExit)
{
Sleep(1000);
}
return 0;
}
int main()
{
HANDLE hWorker = CreateThread(NULL, 0, Worker, NULL, 0, NULL);
Sleep(1000);
g_bExit = TRUE;
Sleep(2000);
if (WAIT_TIMEOUT == WaitForSingleObject(hWorker, -1))
{
//希望編譯器對Worker線程中的g_bExit優化到寄存器中
printf("test ok! ");
}
else
{
//結果沒有
printf("test failed ");
}
return 0;
}
後來google了一下,發現有很多轉載了都是DDJ上面的一篇老文:volatile - Multithreaded Programmer's Best Friend(發現件很可恥的事情,編程世界網站(www.ibiancheng.cn)去年12月發的文,居然寫自己是原創,強烈BS之)。不過這篇文章上的代碼測試了一下,也沒有出現問題,也許是編譯器智能些了?不明白。不管怎樣,多線程開發中,還是要合理利用volatile關鍵字,避免出現不必要的麻煩。
關於異常處理,VC6中區分同步異常處理和異步異常處理兩種,默認的是同步異常處理。C++認爲異常僅在顯示throw或者調用了函數時纔會發生,也就是同步異常處理,所以在Release版本中對同步的異常處理作了優化,把這部分認爲沒用的處理去掉了。對於異步異常處理,可以認爲每個語句都可能發生異常,加上/EHa編譯項就可以了。
呼~,總算完了,自己都感覺寫得有些亂,湊合着看吧。