接著上一遍文章<<編程精粹--編寫高質量C語言代碼(2):自己設計並使用斷言(一)>>,繼續學習如何自己設計並使用斷言,來更加容易,更加不費力地自動尋找出程序中的錯誤。
首先看一個簡單的壓縮還原程序:
byte* pbExpand(byte *pbFrom,byte *pbTo,size_t sizeFrom) { byte b, *bpEnd; size_t size; pbEnd=pbFrom+sizeFrom; while(pbFrom0) *pbTo++=b; } else { *pbTo++=b; } /** 原文中以下代碼沒有被注釋,個人感覺有問題 */ // return pbTo; } /** 原文中沒有這行代碼 */ return pbTo; }
仔細思考一下,上面一個程序進行一次譯碼,總共需要三個字節。所以壓縮程序不應該對兩個連續的字符進行壓縮,當然對連續三個字符進行壓縮也沒有什麼好處。所以壓縮程序應該只對連續三個以上的字符進行壓縮。還有一個情況,就是如果原始數據中含有bReatCode,就必須對其進行特殊處理,否則解壓程序會誤以為它是一個壓縮字符序列的開始。所以當原始數據中出現了bReatCode時,就把它再重復一次,以便和真正的壓縮字符序列區別。
所以我們可以使用斷言來對這兩個特性進行檢驗:
ASSERT(size>=4||(size==1&&b==Reaptcode));
如果斷言失敗說明pbFrom所指向的數據有問題或者壓縮程序有問題。所以利用斷言,我們不僅可以檢查語法上不可能發生的情況,而且可以利用斷言來檢驗程序邏輯上不可能發生的錯誤。
利用斷言來檢查不可能發生的情況。
讓我們接下來看字符解壓程序的另一個版本:
byte* pbExpand(byte *pbFrom,byte *pbTo,size_t sizeFrom) { byte b, *bpEnd; size_t size; pbEnd=pbFrom+sizeFrom; while(pbFrom!=pbEnd) { b=*pbFrom++ if(b==bRepeatCode) { /**在pbTo開始的位置存儲"size"個b */ b=*pbFrom++; size=(size_t)*pbFrom++; /** 檢查原始數據的有效性 */ ASSERT(size>3||(size==1&&b==bReaptCode)); do *pbTo++=b; /** 原文是 while(size--!=0), 個人感覺有問題,因為會多循環一次 於是改成如下語句 */ while(--size!=0); } else { *pbTo++=b; } /** 原文中以下代碼沒有被注釋,個人感覺有問題 */ // return pbTo; } /** 原文中沒有這行代碼 */ return pbTo; }
似乎第一個版本更加合理,也更加聰明,但是如果出於某種原因pbFrom被加過了pbEnd,第一個版本的程序可以在程序造成過多的損害之前,它就會退出,而第二個版本的程序則會企圖對整個內存中的內容進行解壓,從而引起程序崩潰,用戶肯定會發現這個錯誤。所以實際情況就是這樣:防錯性程序設計雖然常常被譽為有較好的編碼風格,但是它卻隱瞞了錯誤。但是這並不意味者我們應該放棄防錯性程序設計。我們希望在進行防錯性程序設計時,錯誤不要被隱瞞。所以對於上面的程序,我們可以一方面一如既往地使用防錯性程序設計,另一方面在事情變槽的情況下利用斷言進行報警。
byte* pbExpand(byte *pbFrom,byte *pbTo,size_t sizeFrom) { byte b, *bpEnd; size_t size; pbEnd=pbFrom+sizeFrom; while(pbFrom0) *pbTo++=b; } else { *pbTo++=b; } /** 原文中以下代碼沒有被注釋,個人感覺有問題 */ // return pbTo; } ASSERT(pbFrom==pbEnd); /** 原文中沒有這行代碼 */ return pbTo; }
在進行防錯性程序設計時,不要隱瞞錯誤。
同時,在編寫代碼時,要抓住一切機會對程序的結果進行驗證。要盡可能地使用不同的算法,而且要使其不僅僅是同一算法的又一實現。如果不同算法產生的結果不同,就會觸發斷言。通過使用不同的算法不僅可以發現算法實現中的錯誤,而且增加了發現算法本身錯誤的可能性。當然這並不意味著每個函數都得有兩個版本,正確的做法是只對程序的關鍵部分這樣做。
要利用不同的算法對程序的結果進行確認。
盡管利用不同算法的執行結果來對程序進行確認,可以幫助我們發現程序中的錯誤。但這畢竟要等到算法執行結束之後才能發現錯誤。有時候程序員應該在程序中進行初始檢查,這樣可以盡快發現錯誤,否則錯誤會隱藏一段時間。
不要等待錯誤發生,要使用初始檢查程序。
總結:
1,防錯性程序設計會隱瞞錯誤。當進行防錯性編碼時如果“不可能發生”的情況確實發生了,要使用斷言報警。
2,利用不同的算法對程序結果進行確認,當同一問題的不同算法出現不同結果時,觸發斷言。
3,使用初始檢查程序,盡早發現程序中的錯誤。
最後以作者的一句話結束這篇文章:
測試者的工作並不只是針對你的程序進行測試,查出自己程序中的錯誤畢竟是你自己的工作。