在使用任何語(yǔ)言進(jìn)行應(yīng)用程序開(kāi)發(fā)時(shí),我們都應(yīng)該提前規(guī)劃好如何處理錯(cuò)誤。Java和c++中普遍使用異常來(lái)進(jìn)行錯(cuò)誤處理,但是c語(yǔ)言,因?yàn)闆](méi)有提供一個(gè)很優(yōu)雅的異常機(jī)制,所以明確如何進(jìn)行錯(cuò)誤處理顯得很重要。C語(yǔ)言中的錯(cuò)誤處理有多種方式,總結(jié)如下:大家可以討論這些處理方式的優(yōu)劣,這樣等以后在程序開(kāi)發(fā)中,我們可以從整體上為程序設(shè)計(jì)更好的錯(cuò)誤處理方法。
1. 返回值方式:用函數(shù)的返回值標(biāo)志函數(shù)是否執(zhí)行成功。比如成功返回1,失敗返回0。這種方式的好處是簡(jiǎn)單方便,而且不影響效率,保持了c語(yǔ)言的高效率。但是仍然有問(wèn)題,一個(gè)問(wèn)題是代碼可讀性的問(wèn)題,如果每個(gè)函數(shù)都有這樣的返回值的話(huà),為了保持程序的正確運(yùn)行,我們必須對(duì)每個(gè)函數(shù)進(jìn)行正確性驗(yàn)證,就是在調(diào)用函數(shù)的時(shí)候檢查他的返回值,這樣程序代碼很大一部分就可能花費(fèi)在錯(cuò)誤處理上。第二個(gè)問(wèn)題就是函數(shù)的返回值沖突的問(wèn)題。假設(shè)strlen函數(shù)也可能會(huì)出錯(cuò),使用這種錯(cuò)誤處理策略他的返回值應(yīng)該標(biāo)志它是否執(zhí)行成功,但是函數(shù)計(jì)算的字符串的長(zhǎng)度值如何自然地傳遞出來(lái)?最后一個(gè)問(wèn)題可能是最重要的:它不強(qiáng)制你處理錯(cuò)誤,而且在不進(jìn)行處理的情況下,程序仍然能夠運(yùn)行,但結(jié)果是不可預(yù)知的。
2.全局errno方式:就是在出現(xiàn)錯(cuò)誤的時(shí)候,將錯(cuò)誤代碼記錄到一個(gè)全局變量errno中。比如waitpid()函數(shù)在被信號(hào)中斷的情況下,將errno設(shè)置為EINTR(一宏定義常量)。這種方式解決了返回值方式遇到的返回值沖突問(wèn)題,而且效率方面也是非常令人愉悅的。但是它要求用戶(hù)在調(diào)用函數(shù)后檢查errno的值,這種保證是脆弱的,程序仍然有可能在不處理那些errno的情況下”安然”地運(yùn)行,導(dǎo)致未定義的結(jié)果。另一個(gè)問(wèn)題出在多線(xiàn)程方面,errno不是線(xiàn)程安全的,多個(gè)線(xiàn)程操作同一個(gè)errno會(huì)造成混亂。
3.錯(cuò)誤封裝:就是將每個(gè)有錯(cuò)誤返回值的函數(shù)分別用一個(gè)函數(shù)包起來(lái),比如waitpid()函數(shù)可以封裝成Waitpid()(首字母大寫(xiě)),在這個(gè)函數(shù)中處理相應(yīng)的錯(cuò)誤。這種錯(cuò)誤處理方法可以很好的解決很多問(wèn)題,應(yīng)該說(shuō)效果很好,但是有幾個(gè)方面需要商榷,一是,并不是每個(gè)函數(shù)的錯(cuò)誤都以一種方式進(jìn)行處理,另一方面,聽(tīng)說(shuō)c語(yǔ)言的函數(shù)調(diào)用開(kāi)銷(xiāo)相對(duì)很高,在函數(shù)外面再包上一層會(huì)影響性能。
4.異常:關(guān)于異常的說(shuō)明和實(shí)現(xiàn)可以參考
它的優(yōu)點(diǎn)是能模擬實(shí)現(xiàn)c++中異常的一些優(yōu)點(diǎn)。但是這個(gè)異常機(jī)制很脆弱,使用時(shí)要注意很多問(wèn)題,而且它的性能開(kāi)銷(xiāo)肯定也會(huì)不小。
5. Goto語(yǔ)句:當(dāng)發(fā)生錯(cuò)誤時(shí),利用goto語(yǔ)句跳到相應(yīng)的錯(cuò)誤處理函數(shù)中。因?yàn)橐恢币詠?lái)對(duì)goto語(yǔ)句的偏見(jiàn),和goto語(yǔ)句本身對(duì)程序結(jié)構(gòu)性的影響,所以本人一直以來(lái)沒(méi)有用過(guò)這種方式,也不知道這種方式會(huì)有什么優(yōu)劣。
總的來(lái)說(shuō),每個(gè)方式都不是盡善盡美的,不知道大家遇到這些問(wèn)題是怎么處理的?
1. 返回值方式:用函數(shù)的返回值標(biāo)志函數(shù)是否執(zhí)行成功。比如成功返回1,失敗返回0。這種方式的好處是簡(jiǎn)單方便,而且不影響效率,保持了c語(yǔ)言的高效率。但是仍然有問(wèn)題,一個(gè)問(wèn)題是代碼可讀性的問(wèn)題,如果每個(gè)函數(shù)都有這樣的返回值的話(huà),為了保持程序的正確運(yùn)行,我們必須對(duì)每個(gè)函數(shù)進(jìn)行正確性驗(yàn)證,就是在調(diào)用函數(shù)的時(shí)候檢查他的返回值,這樣程序代碼很大一部分就可能花費(fèi)在錯(cuò)誤處理上。第二個(gè)問(wèn)題就是函數(shù)的返回值沖突的問(wèn)題。假設(shè)strlen函數(shù)也可能會(huì)出錯(cuò),使用這種錯(cuò)誤處理策略他的返回值應(yīng)該標(biāo)志它是否執(zhí)行成功,但是函數(shù)計(jì)算的字符串的長(zhǎng)度值如何自然地傳遞出來(lái)?最后一個(gè)問(wèn)題可能是最重要的:它不強(qiáng)制你處理錯(cuò)誤,而且在不進(jìn)行處理的情況下,程序仍然能夠運(yùn)行,但結(jié)果是不可預(yù)知的。
2.全局errno方式:就是在出現(xiàn)錯(cuò)誤的時(shí)候,將錯(cuò)誤代碼記錄到一個(gè)全局變量errno中。比如waitpid()函數(shù)在被信號(hào)中斷的情況下,將errno設(shè)置為EINTR(一宏定義常量)。這種方式解決了返回值方式遇到的返回值沖突問(wèn)題,而且效率方面也是非常令人愉悅的。但是它要求用戶(hù)在調(diào)用函數(shù)后檢查errno的值,這種保證是脆弱的,程序仍然有可能在不處理那些errno的情況下”安然”地運(yùn)行,導(dǎo)致未定義的結(jié)果。另一個(gè)問(wèn)題出在多線(xiàn)程方面,errno不是線(xiàn)程安全的,多個(gè)線(xiàn)程操作同一個(gè)errno會(huì)造成混亂。
3.錯(cuò)誤封裝:就是將每個(gè)有錯(cuò)誤返回值的函數(shù)分別用一個(gè)函數(shù)包起來(lái),比如waitpid()函數(shù)可以封裝成Waitpid()(首字母大寫(xiě)),在這個(gè)函數(shù)中處理相應(yīng)的錯(cuò)誤。這種錯(cuò)誤處理方法可以很好的解決很多問(wèn)題,應(yīng)該說(shuō)效果很好,但是有幾個(gè)方面需要商榷,一是,并不是每個(gè)函數(shù)的錯(cuò)誤都以一種方式進(jìn)行處理,另一方面,聽(tīng)說(shuō)c語(yǔ)言的函數(shù)調(diào)用開(kāi)銷(xiāo)相對(duì)很高,在函數(shù)外面再包上一層會(huì)影響性能。
4.異常:關(guān)于異常的說(shuō)明和實(shí)現(xiàn)可以參考
它的優(yōu)點(diǎn)是能模擬實(shí)現(xiàn)c++中異常的一些優(yōu)點(diǎn)。但是這個(gè)異常機(jī)制很脆弱,使用時(shí)要注意很多問(wèn)題,而且它的性能開(kāi)銷(xiāo)肯定也會(huì)不小。
5. Goto語(yǔ)句:當(dāng)發(fā)生錯(cuò)誤時(shí),利用goto語(yǔ)句跳到相應(yīng)的錯(cuò)誤處理函數(shù)中。因?yàn)橐恢币詠?lái)對(duì)goto語(yǔ)句的偏見(jiàn),和goto語(yǔ)句本身對(duì)程序結(jié)構(gòu)性的影響,所以本人一直以來(lái)沒(méi)有用過(guò)這種方式,也不知道這種方式會(huì)有什么優(yōu)劣。
總的來(lái)說(shuō),每個(gè)方式都不是盡善盡美的,不知道大家遇到這些問(wèn)題是怎么處理的?

