窥探try ... catch与__try ... __except的区别
VC中的這兩個東西肯定誰都用過, 不過它們之間有什么區別, 正好有時間研究了一下, 如果有錯誤歡迎拍磚.
基于VC2005, 32位XP 平臺測試通過. 估計對于其他版本的VC和操作系統是不通用的.
1. try ... catch
這個是C++語言定義的, 每個C++都有對其的不同的實現. 使用也很簡單. 比如我們有一個函數, 讀入年齡. 如果<=0 或者 >=100, 拋出異常:
int readAge() {
?? int age = 讀入年齡;
?? if (age <=0 || age >= 100) {
????? throw AgeException(age);
?? }
?? return age;
}
其中 AgeException 的定義為
class AgeException {
?? public:
?? int errorAge;
?? AgeException(int age) {
????? errorAge = age;
?? }
};
在使用的時候也比較簡單,
try {
?? int i = readAge();
?? printf("Age inputed is %d", i);
} catch (AgeException e) {
?? printf("error. Age inputed = %d and is not valid.", e.errorAge);
}
2. __try ... __except
這個是VC自己定義的不是C++的關鍵字. VC在編譯__try ... __except的時候, 會按照Windows SEH(結構化異常)處理的規則, 把異常處理部分加入到當前線程的異常處理鏈中. 這部分不詳細寫了, SEH處理在網上的文章一搜一大把.
3. try...catch 與 __try ... __except 使用上的區別
對于上面的AgeException, 我們也可以使用__try... __except 來處理:
__try {
?? int i = readAge();
?? printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
?? printf("Age is not correct.");
}
但是, 對于__try ... __except 能夠處理的異常(比如下面的代碼), C++異常處理try .. catch 不能夠捕獲(Catch段不能執行):
try {
?? int *p = NULL;
?? *p = 0;
} catch (...) {
?? printf("Exception occured.");
}
注: 這里其實和編譯器有關, VC2005由/EH加上參數來控制, 詳情參見http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx . 這里討論的是默認的情況, 不處理的時候.
這是為什么呢. 仔細看了下, 當我們在程序里面throw出來一個異常的時候, 調試器(比如VC, WinDBG)會記錄下面一個事件:
First-chance exception at 0x7c812afb (kernel32.dll) in trycatch.exe:?Microsoft C++ exception: AgeException at memory location 0x0012fc98..
也就是說, 在VC中, throw出來的都是Microsoft C++ exception. 只有這種Exception才能被try...catch捕獲. 同樣, 用WinDBG裝載上面的程序
__try {
?? int i = readAge();
?? printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
?? printf("Age is not correct.");
}
會發現, 出現的異常為C++ exception, 異常代碼為0xe06d7363:
也就是說, 在C++中throw出來的異常是一種特殊的類型的異常, 是微軟專門為VC++實現的,異常代碼為0xe06d7363. (有意思的是ASCII碼為0x6d, 0x73, 0x63的字符為msc)
到這里我們基本可以得出一個結論, try...catch和__try...__except其實從本質上來說是一回事, 他們從根源上來說都是用到了Windows的SEH處理機制. 不同點在于:
-) try...catch 只處理異常代碼為0xe06d7363的C++ exception, 不會理會其他的;
-) try...catch 對于編譯器來說做了一些額外的工作, 但是最終的實現是和__try...__except都要歸結于SEH
-) try...catch 多了一些額外的傳遞具體的異常信息的部分(catch的是何種異常. 不像是__try...__except, 需要用ExceptionCode去判斷)
想到這, 想到了下面一個問題, 就是VC++編譯器是如何知道catch的異常信息的呢? 換句話說, 對于下面的代碼, 我們知道出現了異常, 但是怎么得到異常的信息的呢?
__try {
?? int i = readAge();
?? printf("Age inputed is %d", i);
} __except (EXCEPTION_EXECUTE_HANDLER) {
?? printf("Age is not correct.");?//如何知道readAge中throw出來的AgeException?
}
為了調試方便, 把異常類和拋出異常的代碼修改一下, 在創建異常的時候傳遞錯誤的age和一條消息
class AgeException {
public:
?? int errorAge;
?? char *p;
?? AgeException(int age, char* msg) {
????? errorAge = age;
????? p = msg;
?? }
};
int readAge() {
?? int age = 123; //
?? if (age <=0 || age >= 100) {
????? throw AgeException(age, "Age is outof range.");
?? }
?? return age;
}
用WinDBG裝入, 運行, 出現了C++異常后, 使用.exr -1?命令查看最近出現的異常:
可以看出, 這個異常為前面討論的C++異常(0xe06d7363類型), 帶有3個參數, 每個參數, 參數分別為0x19930520, 0x0012fca4, 0x00417bc8. 因為我沒有找到C++異常中參數的含義, 只能猜了(哪位如果知道請賜教).
考慮到拋出異常的代碼拋出的異常類型為AgeException, 那么很自然想到拋出的異常作為一個指針存儲在參數中. 因為沒有資料, 只能挨個試驗了. 使用命令dt trycatch!AgeException 地址, 來把trycatch模塊(編譯出的程序名是trycatch.exe)中地址的內容按照類AgeException顯示出來:
果然, 第一個參數0x19930520里面是不是我們想要的; 當輸入第二個參數的時候, 該地址中的內容和預料的一致, 是我們拋出的異常中的內容. 這樣驗證了猜想. try...catch的工作流程為:
-) 編譯器在編譯try...catch的時候, 也是利用Windows的SEH, 只不過僅僅針對C++異常(0xe06d7363類型)進行處理;
-) 拋出異常的時候(throw), 把生成的異常類的實例地址, 保存在異常信息的第二個參數中
-) catch異常的時候, 從異常信息第2個參數中讀出地址, 并轉化為異常類的實例, 供程序使用.
要使用__try...__except模擬上述的過程, 程序可以改為:
__try {
?? int i = readAge();
?? printf("Age inputed is %d", i);
} __except (extract(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER) {
?? printf("Exception happene.");
}
void extract(LPEXCEPTION_POINTERS p) {
?? int d = p->ExceptionRecord->NumberParameters; //參數數目, 這里沒用到
?? unsigned int * ex = (unsigned int *) p->ExceptionRecord->ExceptionInformation[1];?//第二個參數
?? AgeException * e = (AgeException *) ex;?//轉換,得到異常類的實例
?? printf("==> %d \n", e->errorAge);?//異常的信息可以知道了.
??? printf("==> %s \n", e->p);?//異常的信息可以知道了.
}
運行, 和預期的結果是一致的.
最后可以得到結論(不知道這樣說是否完全正確) :
try...catch是編譯器對__try ... __except的一個包裝; 該包裝僅處理C++異常類型, 但是提供了比較方便的方法來傳遞拋出的異常信息, 這樣程序員能夠比較方便的處理異常, 而不用想上面的例子那樣要手工去異常信息中去取.
轉載于:https://www.cnblogs.com/zhangdongsheng/p/3857509.html
總結
以上是生活随笔為你收集整理的窥探try ... catch与__try ... __except的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS集成alipay无线应用sdk错误
- 下一篇: JSP中文乱码问题终极解决方案(转)