c/c++異常處理機制-i
2010-12-07 17:23:44
分類: c/c++
引言:
visual c++提供了對c語言、c++語言及mfc的支援,因而其涉及到的異常(exception)處理也包含了這三種型別,即c語言、c++語言和mfc的異常處理。除此之外,微軟對c和c++的異常處理進行了擴充套件,提出了結構化異常處理(seh)的概念,它支援c和c++(與之相比,mfc異常處理僅支援c++)。
乙個典型的異常處理包含如下幾個步驟:
(1)程式執行時發生錯誤;
(2)以乙個異常物件(最簡單的是乙個整數)記錄錯誤的原因及相關資訊;
(3)程式檢測到這個錯誤(讀取異常物件);
(4)程式決定如何處理錯誤;
(5)進行錯誤處理,並在此後恢復/終止程式的執行。
c、c++、mfc及seh在這幾個步驟中表現出了不同的特點。本文將對這四種異常處理進行介紹,並對它們進行對比分析。本文例程的除錯平台為visual c++6.0,作業系統為windows xp,所有程式均除錯通過。
在進入正式的講解之前,先說幾句廢話。許多的程式設計新手對異常處理視而不見,程式裡很少考慮異常情況。一部分人甚至根本就不考慮,以為程式總是能以正確的途徑執行。譬如我們有的程式設計者呼叫fopen開啟乙個檔案後,立馬就開始進行讀寫操作,根本就不考慮檔案是否正常開啟了。這種習慣一定要改掉,縱使你再不願意!這是軟體健壯性的需要!異常處理不是浪費時間!
1.c語言異常處理
1.1 異常終止
標準c庫提供了abort()和exit()兩個函式,它們可以強行終止程式的執行,其宣告處於標頭檔案中。這兩個函式本身不能檢測異常,但在c程式發生異常後經常使用這兩個函式進行程式終止。下面的這個例子描述了exit()的行為:
#include
#include
int main(void)
在這個例子中,main函式一開始就執行了exit函式(此函式原型為void exit(int)),因此,程式不會輸出"程式不會執行到這裡"。程式中的exit(exit_success)表示程式正常結束,與之對應的exit(exit_failure)表示程式執行錯誤,只能強行終止。exit_success、exit_failure分別定義為0和1。
對於exit函式,我們可以利用atexit函式為exit事件"掛接"另外的函式,這種"掛接"有點類似windows程式設計中的"鉤子"(hook)。譬如:
#include
#include
static void atexitfunc(void)
int main(void)
程式輸出"atexit掛接的函式"後即終止。來看下面的程式,我們不呼叫exit函式,看看atexit掛接的函式會否執行:
#include
#include
static void atexitfunc(void)
int main(void)
程式輸出:
不呼叫exit函式
atexit掛接的函式
這說明,即便是我們不呼叫exit函式,當程式本身退出時,atexit掛接的函式仍然會被執行。
atexit可以被多次執行,並掛接多個函式,這些函式的執行順序為後掛接的先執行,例如:
#include
#include
static void atexitfunc1(void)
static void atexitfunc2(void)
static void atexitfunc3(void)
int main(void)
輸出的結果是:
atexit掛接的函式3
atexit掛接的函式2
atexit掛接的函式1
在visual c++中,如果以abort函式(此函式不帶引數,原型為void abort(void))終止程式,則會在debug模式執行時彈出debug除錯對話方塊。
1.2 斷言(assert)
assert巨集在c語言程式的除錯中發揮著重要的作用,它用於檢測不會發生的情況,表明一旦發生了這樣的情況,程式就實際上執行錯誤了,例如strcpy函式:
char *strcpy(char *strdest, const char *strsrc)
其中包含斷言assert( (strdest != null) && (strsrc != null) ),它的意思是源和目的字串的位址都不能為空,一旦為空,程式實際上就執行錯誤了,會引發乙個abort。
assert巨集的定義為:
#ifdef ndebug
#define assert(exp) ((void)0)
#else
#ifdef __cplusplus
extern "c"
#endif
#define assert(exp) (void)( (exp) || (_assert(#exp, __file__, __line__), 0) )
#endif /* ndebug */
如果程式不在debug模式下,assert巨集實際上什麼都不做;而在debug模式下,實際上是對_assert() 函式的呼叫,此函式將輸出發生錯誤的檔名、**行、條件表示式。例如下列程式:
#include
#include
#include
char * mystrcpy( char *strdest, const char *strsrc )
int main(void)
在此程式中,為了避免我們的strcpy與c庫中的strcpy重名,將其改為mystrcpy。斷言失敗,這是因為_assert()函式中也呼叫了abort()函式。
一定要記住的是assert本質上是乙個巨集,而不是乙個函式,因而不能把帶有***的表示式放入assert的"引數"中。
1.3 errno
errno在c程式中是乙個全域性變數,這個變數由c執行時庫函式設定,使用者程式需要在程式發生異常時檢測之。c執行庫中主要在math.h和stdio.h標頭檔案宣告的函式中使用了errno,前者用於檢測數**算的合法性,後者用於檢測i/o操作中(主要是檔案)的錯誤,例如:
#include
#include
#include
int main(void)
else
return 0;
} 在此程式中,如果檔案開啟失敗(fopen返回null),證明發生了異常。我們讀取error可以獲知錯誤的原因,如果d盤根目錄下不存在"1.txt"檔案,將輸出2,表示檔案不存在;在檔案存在並正確開啟的情況下,將執行到else語句,輸出0,證明errno沒有被設定。
visual c++提供了兩種版本的c執行時庫。乙個版本供單執行緒應用程式呼叫,另乙個版本供多執行緒應用程式呼叫。多執行緒執行時庫與單執行緒執行時庫的乙個重大差別就是對於類似errno的全域性變數,每個執行緒單獨設定了乙個。因此,對於多執行緒的程式,我們應該使用多執行緒c執行時庫,才能獲得正確的error值。
另外,在使用errno之前,我們最好將其設定為0,即執行errno = 0的賦值語句。
1.4 其它
除了上述異常處理方式外,在c語言中還支援非區域性跳轉(使用setjmp和longjmp)、訊號(使用signal 、raise)、返回錯誤值或回傳錯誤值給引數等方式進行一定能力的異常處理,但是其使用不如1.1~1.3節所介紹方式常用,我們不必過細研究。
從以上分析可知,c語言的異常處理是簡單而不全面的。與c++的異常處理比起來,c語言異常處理相形見絀,它就像娘胎裡的雛嬰。
C C 異常處理機制 I
引言 visual c 提供了對c語言 c 語言及mfc的支援,因而其涉及到的異常 exception 處理也包含了這三種型別,即c語言 c 語言和mfc的異常處理。除此之外,微軟對c和c 的異常處理進行了擴充套件,提出了結構化異常處理 seh 的概念,它支援c和c 與之相比,mfc異常處理僅支援c...
C C 異常處理機制 II
二.c 語言異常處理 2.1 c 異常處理語法 感謝c 語言的後期改造者們,他們在標準c 語言中專門整合了異常處理的相關語法 與之不同的是,所有的c 標準庫異常體系都需要執行庫的支援,它不是語言核心支援的 當然,異常處理被加到程式語言中,也是程式語言發展和逐步完善的必然結果。我們看到,c 不是唯一整...
C C 異常處理機制 II
二.c 語言異常處理 2.1 c 異常處理語法 感謝c 語言的後期改造者們,他們在標準c 語言中專門整合了異常處理的相關語法 與之不同的是,所有的c 標準庫異常體系都需要執行庫的支援,它不是語言核心支援的 當然,異常處理被加到程式語言中,也是程式語言發展和逐步完善的必然結果。我們看到,c 不是唯一整...