在使用Windows程式的時候,相信沒有使用者喜歡看到程式突然崩潰的情形吧!為了避免程式崩潰,程式員在編寫程式的時候最好在容易出錯的地方使用異常處理機制來保證友好的使用者體驗。特別是編寫C/C++代碼的時候,崩潰是經常的事情!
今天一位同事給我說,編寫C/C++代碼崩潰的原因主要是因為記憶體操作違規。如果每次操作一塊記憶體或指標之前都檢查記憶體或指標是否有效,那麼可以降低程式崩潰的次數。但是這會讓程式員很厭煩的,哈哈。所以在適當的地方加上異常處理,即使崩潰也會讓程式員更好的改善程式。當然,程式效率必然降低!
幸好C++規範中有異常處理機制: try catch
但是在Visual Studio中直接使用try catch是不能產生異常的,必須手動拋出異常throw,見如下代碼:
void TryCatchfourth()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
throw 0; // 這裡我是隨便拋出異常以測試
// 若沒有手動throw,後面即使產生異常catch裡面的代碼還是不會被執行
puts("No "); // 接下來這兩句不會被執行
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Fourth");
}
又下面這段代碼,如果將vs編譯器的選項修改:開啟項目屬性→配置屬性→C/C++→代碼產生→啟用C++異常→是,但有SEH異常(/EHa) (這裡編譯器預設為"是(/EHsc)")。
void TryCatchThree()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Three");
}
修改編譯器選項之後執行TryCatchThree()就正常工作。請注意TryCatchThree()和TryCatchfourth()所採用的不同編譯選項。
最後參考一個Windows的結構化異常處理代碼:
void TryCatchSecond()
{
char* Test = NULL;
__try
{
__try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
// 此處數字一定要大,否則不能產生異常。
// 因為Windows平台下,new N個位元組,系統分配的一定比N大。
// 4096大小剛好是一個頁面大小,如果N>4096,那就自己擴大對應數字以產生異常
}
__finally
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("2 "); // this is printed second
}
}
__except ( FilterFunction(GetExceptionCode()) )
{
printf("3 \n"); // this is printed last
}
puts("Second");
}
不管編譯器選項是否按照上述要求被修改,TryCatchSecond()均能正常工作。同事說,這是Windows結構化異常,和C++中的異常有點不一樣,我現在也不太懂,以後查到資料後在添加到部落格中來。TryCatchSecond()代碼是參考MSDN中的一個例子,如下:
DWORD FilterFunction(int i = 1)
{
printf("%d ", i); // printed first
return EXCEPTION_EXECUTE_HANDLER;
}
void TryCatchFirst()
{
char* Test = NULL;
__try
{
__try
{
// 這個API是手動設定異常代碼(這麼稱呼有點彆扭)
RaiseException(1, // exception code
0, // continuable exception
0, NULL); // no arguments
}
__finally
{
printf("2 "); // this is printed second
}
}
__except ( FilterFunction() )
{
printf("3 \n"); // this is printed last
}
puts("One");
}
下面是完整代碼:(本文中代碼均在vs2008中編寫和測試,建議不要在vc6下測試,vc6對C++規範支援很不好)
View Code
#include <windows.h>
// 釋放數組記憶體
#define FreeArray(pArray) { \
delete[] pArray; \
pArray = NULL; \
}
DWORD FilterFunction(int i = 1)
{
printf("%d ", i); // printed first
return EXCEPTION_EXECUTE_HANDLER;
}
void TryCatchFirst()
{
char* Test = NULL;
__try
{
__try
{
// 這個API是手動設定異常代碼(這麼稱呼有點彆扭)
RaiseException(1, // exception code
0, // continuable exception
0, NULL); // no arguments
}
__finally
{
printf("2 "); // this is printed second
}
}
__except ( FilterFunction() )
{
printf("3 \n"); // this is printed last
}
puts("One");
}
void TryCatchSecond()
{
char* Test = NULL;
__try
{
__try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
// 此處數字一定要大,否則不能產生異常。
// 因為Windows平台下,new N個位元組,系統分配的一定比N大。
// 4096大小剛好是一個頁面大小,如果N>4096,那就自己擴大對應數字以產生異常
}
__finally
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("2 "); // this is printed second
}
}
__except ( FilterFunction(GetExceptionCode()) )
{
printf("3 \n"); // this is printed last
}
puts("Second");
}
void TryCatchThree()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Three");
}
void TryCatchfourth()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
throw 0; // 這裡我是隨便拋出異常以測試
// 若沒有手動throw,後面即使產生異常catch裡面的代碼還是不會被執行
puts("No "); // 接下來這兩句不會被執行
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Fourth");
}
VOID main(VOID)
{
// TryCatchFirst();
// TryCatchSecond();
// TryCatchThree();
// TryCatchfourth();
}
【參考資料 感謝作者】
1、我的同事