c语言常用的异常处理,C语言中的异常处理
一 前言:
異常處理,對于做面向?qū)ο箝_發(fā)的開發(fā)者來說是再熟悉不過了,例如在C#中有
try
{
...
}
catch( Exception e){...}
finally{
.....
}
在C++中,我們常常會使用
try{}
...
catch(){}
塊來進(jìn)行異常處理。
說了那么多,那么到底什么是異常處理呢?
異常處理(又稱為錯誤處理)功能提供了處理程序運(yùn)行時出現(xiàn)的任何意外或異常情況的方法。
異常處理一般有兩種模型,一種是"終止模型",一種是"恢復(fù)模型"
"終止模型":在這種模型中,將假設(shè)錯誤非常關(guān)鍵,將以致于程序無法返回到異常發(fā)生的地方繼續(xù)執(zhí)行.一旦異常被拋出,就表明錯誤已無法挽回,也不能回來繼續(xù)執(zhí)行.
"恢復(fù)模型":異常處理程序的工作是修正錯誤,然后重新嘗試調(diào)動出問題的方法,并認(rèn)為的二次能成功. 對于恢復(fù)模型,通常希望異常被處理之后能繼續(xù)執(zhí)行程序.在這種情況下,拋出異常更像是對方法的調(diào)用--可以在Java里用這種方法進(jìn)行配置,以得到類似恢復(fù)的行為.(也就是說,不是拋出異常,而是調(diào)用方法修正錯誤.)或者,把try塊放在while循環(huán)里,這樣就可以不斷的進(jìn)入try塊,直到得到滿意的結(jié)果.
二 面向?qū)ο笾械漠惓L幚?/p>
大致了解了什么是異常處理后,由于異常處理在面向?qū)ο笳Z言中使用的比較普遍,我們就先以C++為例,做一個關(guān)于異常處理的簡單例子:
問題:求兩個數(shù)相除的結(jié)果。
這里,隱藏這一個錯誤,那就是當(dāng)除數(shù)為0時,會出現(xiàn),所以,我們得使用異常處理來捕捉這個異常,并拋出異常信息。
具體看代碼:
1?#include?
2?#include?
3?using?namespace?std;
4?class?DivideError:public?exception
5?{
6??public:
7???????????DivideError::DivideError():exception(){}
8??????????const?char*?what(){
9?????????????return?"試圖去除一個值為0的數(shù)字";
10?????????}
11
12?};
13?double?quotion(int?numerator,int?denominator)
14?{
15?????if(0==denominator)??????????//當(dāng)除數(shù)為0時,拋出異常16?????throw?DivideError();
17?????return?static_cast(numerator)/denominator;
18?}
19?int?main()
20?{
21?????int?number1;?????????????//第一個數(shù)字22?????int?number2;?????????????//第二個數(shù)字23?????double?result;
24?????cout<
25?????while(cin>>number1>>number2){
26?????????try{
27?????????????result=quotion(number1,number2);
28?????????????cout<
29
30?????????}?????//end?try31?????????catch(DivideError?&divException){
32?????????????cout<
33?????????????????<
34?????????}
35?????}
36
37?}
38
在這個例子中,我們使用了頭文件中的exception類,并使DivideError類繼承了它,同時重載了虛方法what(),以給出特定的異常信息。
而C#中的異常處理類則封裝的更有全面,里面封裝了常用的異常處理信息,這里就不多說了。
三 C語言中的異常處理
在C語言中異常處理一般有這么幾種方式:
1.使用標(biāo)準(zhǔn)C庫提供了abort()和exit()兩個函數(shù),它們可以強(qiáng)行終止程序的運(yùn)行,其聲明處于頭文件中。
2.使用assert(斷言)宏調(diào)用,位于頭文件中,當(dāng)程序出錯時,就會引發(fā)一個abort()。
3.使用errno全局變量,由C運(yùn)行時庫函數(shù)提供,位于頭文件中。
4.使用goto語句,當(dāng)出錯時跳轉(zhuǎn)。
5.使用setjmp,longjmp進(jìn)行異常處理。
接下來,我們就依次對這幾種方式來看看到底是怎么做的:
我們?nèi)耘f以前面處理除數(shù)為0的異常為例子。
1.使用exit()函數(shù)進(jìn)行異常終止:
1?#include?
2?#include?
3?double?diva(double?num1,double?num2)?????????//兩數(shù)相除函數(shù)4?{
5?????double?re;
6?????re=num1/num2;
7?????return?re;
8?}
9?int?main()
10?{
11????double?a,b,result;
12??printf("請輸入第一個數(shù)字:");
13???scanf("%lf",&a);
14???printf("請輸入第二個數(shù)字:");
15???scanf("%lf",&b);
16???if(0==b)????????????????????????????????//如果除數(shù)為0終止程序17???exit(EXIT_FAILURE);
18?result=diva(a,b);
19????printf("相除的結(jié)果是:?%.2lf\n",result);
20?return?0;
21?}
其中exit的定義如下:
_CRTIMP void __cdecl __MINGW_NOTHROW?exit?(int) __MINGW_ATTRIB_NORETURN;
exit的函數(shù)原型:void exit(int)由此,我們也可以知道EXIT_FAILURE宏應(yīng)該是一個整數(shù),exit()函數(shù)的傳遞參數(shù)是兩個宏,一個是剛才看到的EXIT_FAILURE,還有一個是EXIT_SUCCESS從字面就可以看出一個是出錯后強(qiáng)制終止程序,而一個是程序正常結(jié)束。他們的定義是:
#define?EXIT_SUCCESS?0
#define?EXIT_FAILURE?1
到此,當(dāng)出現(xiàn)異常的時候,程序是終止了,但是我們并沒有捕獲到異常信息,要捕獲異常信息,我們可以使用注冊終止函數(shù)atexit(),它的原型是這樣的:int atexit(atexit_t func);
具體看如下程序:
1?#include?
2?#include?
3?void?Exception(void)???????????????????????????//注冊終止函數(shù),通過掛接到此函數(shù),捕獲異常信息4?{
5?????printf("試圖去除以一個為0的數(shù)字,出現(xiàn)異常!\n");
6?}
7?int?main()
8?{
9????double?a,b,result;
10???printf("請輸入第一個數(shù)字:");
11???scanf("%lf",&a);
12???printf("請輸入第二個數(shù)字:");
13???scanf("%lf",&b);
14???if(0==b)????????????????????//如果除數(shù)為0終止程序?,并掛接到模擬異常捕獲的注冊函數(shù)15???{
16
17???atexit(Exception);
18???exit(EXIT_FAILURE);
19???}
20????result=diva(a,b);
21????printf("相除的結(jié)果是:?%.2lf\n",result);
22?return?0;
23?}
這里需要注意的是,atexit()函數(shù)總是被執(zhí)行的,就算沒有exit()函數(shù),當(dāng)程序結(jié)束時也會被執(zhí)行。并且,可以掛接多個注冊函數(shù),按照堆棧結(jié)構(gòu)進(jìn)行執(zhí)行。abort()函數(shù)與exit()函數(shù)類似,當(dāng)出錯時,能使得程序正常退出,這里就不多說了。
2.使用assert()進(jìn)行異常處理:
assert()是一個調(diào)試程序時經(jīng)常使用的宏,切記,它不是一個函數(shù),在程序運(yùn)行時它計(jì)算括號內(nèi)的表達(dá)式,如果表達(dá)式為FALSE ?(0), ?程序?qū)?bào)告錯誤,并終止執(zhí)行。如果表達(dá)式不為0,則繼續(xù)執(zhí)行后面的語句。這個宏通常原來判斷程序中是否出現(xiàn)了明顯非法的數(shù)據(jù),如果出現(xiàn)了終止程序以免導(dǎo)致嚴(yán)重后果,同時也便于查找錯誤。
另外需要注意的是:assert只有在Debug版本中才有效,如果編譯為Release版本則被忽略。
我們就前面的問題,使用assert斷言進(jìn)行異常終止操作:構(gòu)造可能出現(xiàn)出錯的斷言表達(dá)式:assert(number!=0)這樣,當(dāng)除數(shù)為0的時候,表達(dá)式就為false,程序報(bào)告錯誤,并終止執(zhí)行。
代碼如下:
代碼
3.使用errno全局變量,進(jìn)行異常處理:
errno全局變量主要在調(diào)式中,當(dāng)系統(tǒng)API函數(shù)發(fā)生異常的時候,將errno變量賦予一個整數(shù)值,根據(jù)查看這個值來推測出錯的原因。
其中的各個整數(shù)值都有一個相應(yīng)的宏定義,表示不同的異常原因:
代碼
這里我們就不以前面的除數(shù)為0的例子來進(jìn)行異常處理了,因?yàn)槲也恢廊绾味x自己特定錯誤的errno,如果哪位知道,希望能給出方法。我以一個網(wǎng)上的例子來說明它的使用方法:
代碼
這里試圖打開一個d盤的文件,如果文件不存在,這是查看errno的值,結(jié)果是2、
當(dāng)文件存在時,errno的值為初始值0。然后查看值為2的錯誤信息,在宏定義那邊#define????ENOFILE????????2????/*?No?such?file?or?directory?*/
便知道錯誤的原因了。
4.使用goto語句進(jìn)行異常處理:
goto語句相信大家都很熟悉,是一個跳轉(zhuǎn)語句,我們還是以除數(shù)為0的例子,來構(gòu)造一個異常處理的例子:
代碼
5.使用setjmp和longjmp進(jìn)行異常捕獲與處理:
setjmp和longjmp是非局部跳轉(zhuǎn),類似goto跳轉(zhuǎn)作用,但是goto語句具有局限性,只能在局部進(jìn)行跳轉(zhuǎn),當(dāng)需要跳轉(zhuǎn)到非一個函數(shù)內(nèi)的地方時就需要用到setjmp和longjmp。setjmp函數(shù)用于保存程序的運(yùn)行時的堆棧環(huán)境,接下來的其它地方,你可以通過調(diào)用longjmp函數(shù)來恢復(fù)先前被保存的程序堆棧環(huán)境。異常處理基本方法:
使用setjmp設(shè)置一個跳轉(zhuǎn)點(diǎn),然后在程序其他地方調(diào)用longjmp跳轉(zhuǎn)到該點(diǎn)(拋出異常).
代碼如下所示:
#include?
#include?
jmp_buf?j;
void?Exception(void)
{
longjmp(j,1);
}
double?diva(double?num1,double?num2)?????????//兩數(shù)相除函數(shù)?{
double?re;
re=num1/num2;
return?re;
}
int?main()
{
double?a,b,result;
printf("請輸入第一個數(shù)字:");
scanf("%lf",&a);
printf("請輸入第二個數(shù)字:");
if(setjmp(j)==0)
{
scanf("%lf",&b);
if(0==b)
Exception();
result=diva(a,b);
printf("相除的結(jié)果是:?%.2lf\n",result);
}
else
printf("試圖除以一個為0的數(shù)字\n");
return?0;
}
四 總結(jié):
除了以上幾種方法之外,另外還有使用信號量等等方法進(jìn)行異常處理。當(dāng)然在實(shí)際開發(fā)中每個人都有各種調(diào)式的技巧,而且這文章并不是說明異常處理一定要這樣做,這只是對一般做法的一些總結(jié),也不要亂使用異常處理,如果弄的不好就嚴(yán)重影響了程序的效率和結(jié)構(gòu),就像設(shè)計(jì)模式一樣,不能胡亂使用。
總結(jié)
以上是生活随笔為你收集整理的c语言常用的异常处理,C语言中的异常处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线程运行程序c语言,理解线程1 C语言示
- 下一篇: c语言ascii图形输出,C语言实例10