編寫優質無錯程式碼(2) (轉)
編寫優質無錯程式碼(2) (轉)[@more@]討論過程中,有人認為assert檢查的是, 而異常是可以恢復的意外情況。
所以,觀點3的支持者說:可恢復的意外是可以理解的,但可恢復的bug就沒什
麼意義了。既然已經約定好了,你再違背,就屬於是bug而不是意外了(比如打
不開什麼的)。很多庫都不檢查指標的合法性(除了以外,因
為總不能讓系統dump core吧),也不檢查指標是否為NULL(因為如果層層都檢
查,必定勞民傷財,乾脆讓最上面呼叫的人在呼叫之外查)。
6、選擇d+f
選f+d, 好處如下:
a以最激烈的方式,充分暴露呼叫都的錯誤!能及時修改BUG
b便於,問題出現後,直接到事故現場。比120還快!
c對於realse版的程式碼沒有任何副作用。
d以處理的代價來看 採用斷言也是編寫最小一種。
e它是多語種,多平臺所通用的方式, 如:C /C++ VB,1.4 在win ,
通吃, 便於移置!
如果在現實中,測試沒有能找到所有的BUG,那可能就要用異常來幫忙了!
當然,我也提出了我的觀點, 我支援觀點6。理由如下:
assert只在debug標誌的時候有用,而在編譯release版本的時候不起作用。
assert對於檢查硬編碼的錯誤,是非常有用的,能夠及時的查處編碼的錯
誤。比如borland c++的類庫中就有很多這樣的assert。但是assert
不是萬能的,因為有很多錯誤的發生不是完全在編譯時發生的,而是執行
時的錯誤。在release後,assert是不可能依賴的。那麼,我們就需要
exception這一機制來檢測執行時錯誤,並相應的做出處理。當然,在異常
檢測和處理過程中還有許多需要討論的問題,由於不是這一題目的範圍,
我們沒有必要繼續討論得太多,但是,提出來希望大家注意:異常不是捕
獲了就完成任務了,而要對於不同的情況,採取不同的處理辦法,千萬不
能只是捕獲,而不做任何處理,那樣和不捕獲異常沒有任何區別。
在題目剛剛提出的時候,選擇各種答案的人都有,所以,我有必要在這裡把
其他答案為什麼不能選的理由說一下。
(a) if (!pParam)
return 0;
這是很多初級員常常採取的一種方式。返回值設為0。 因為函式的返回
值往往是計算的結果,不贊成把錯誤標誌值和計算結果混在一起使用,容易
造成使用者的誤會。當然,在很多unix函式中,由於歷史原因,還存在很多
這樣子的函式,所以需要指出,不要沿用這種方式。
(b) if (!pParam)
return ERROR_PARAM;
b比a稍微好一點點,返回了一個常量或者預定義的宏。 從返回值的字面上,
呼叫者能知道發生了什麼錯誤,但是,這也不是一種好的方法。
(c) if (!pParam)
pParam = "";
...
這是最不好的方式。直接給pParam賦予空字串,然後繼續函式過程,這
容易造成不可預料的後果,是程式不穩定的根源。
(d) if (!pParam)
throw EXCEPTION_ERROR_PARAM;
丟擲異常,剛剛已經討論過了,不再贅述。
(e) if (!pParam)
MessageBox(...);
這是一種比較可笑的方式,當然也有不少人用。MessageBox是直接彈出一個
對話方塊,告訴使用者,出錯了。但是並不做任何處理,程式繼續往下,
直到出錯崩潰。呵呵
(f) assert(!pParam);
斷言,剛剛已經討論過了,不再贅述。
以上這個題目,引發了所有與會者的興趣,討論異常熱烈,最後,主持人
也給出了自己的觀點:d+f。當然這並不是標準答案,因為這一門課程
本來就沒有什麼標準答案,大家見仁見智,這個答案只是的積累。
主持人緊接著列出了"編寫優質無錯程式碼的經驗":
a.理想的和實際的編譯器
b.使用斷言
c.函式的介面設計
d.考慮風險
e.態度的問題
以上是本節的主要內容。斷言,剛剛的問題中已經討論過了,來看看其他的
內容。
理想的編譯器和實際的編譯器:
題目二:
下面memcpy函式實現有什麼問題:
Void *memcpy(void *pvTo,void *pvFrom,size_t size){
byte *pbTo=(byte *) pvTo;
byte *pbFrom=(byte *)pvFrom;
while(size -- >0);
*pbTo++= *pbFrom++;
return pvTo;
}
呵呵,粗略一看,這函式還真找不出問題來。但是仔細看看,你就會發現
while(size -- >0);
這裡多了一個分號,導致下面的*pbTo++= *pbFrom++;不是在while迴圈中
執行多次,而是隻執行了一次。當然這不是函式設計者的預期結果,而編
譯器卻不會報告錯誤,因為while(size -- >0);從語法上來講,並沒有
錯誤。這就是理想的編譯器和實際的編譯器的區別所在。
那麼,該怎麼檢查這種錯誤呢?主持人提出瞭如下辦法:
while(size -- >0) NULL; 可以加入NULL來解決空語句. 這樣子,當你需
要 while(size -- >0)
*pbTo++= *pbFrom++;
這種形式的時候,就不會發生錯誤了,只需要用眼睛看看,就能發現了。
兩點好處 1 無冗餘程式碼,2 使人更明白。減少風險.
還有人會這麼寫
if( (n=read(....)) == 1) ....
在這裡,賦值符號=和判斷相等的符號==容易敲錯,而編譯器又檢查不出來,
可能就會有如下錯誤:
If(ch = ‘ ’)...;這也是需要注意的問題。
理想的編譯器和實際的編譯器小結:
a.把屢次出錯的合法的C習慣用法看成程式中的錯誤
b.增強編譯器的警告級別
c.使用其它的工具來檢查程式碼 如 Lint 等
d.進行單元測試
e.消除程式錯誤的最好方法是儘可能早、儘可能容易地發現錯誤,要尋求費力最小的自動查錯的方法
f.努力減少程式設計師查錯所需的技巧
使用斷言
題目三
下面函式實現,哪一個好,為什麼?
a.
char Uptolower(char ch){
if(ch >= ‘A’ && ch <= ‘Z’)
return ch+=‘a’-’A’;
return -1;
}
b.
char Uptolower(char ch){
assert(ch >= ‘A’ && ch <= ‘Z’);
if(ch >= ‘A’ && ch <= ‘Z’)
return ch+=‘a’-’A’;
return ch;
}
c.
char Uptolower(char ch){
assert(ch >= ‘A’ && ch <= ‘Z’);
return ch+(‘a’-’A’);
}
分析:
a.該函式檢查ch是否在A..Z之間,如果是,則返回相應的小寫字元,如果
所以,觀點3的支持者說:可恢復的意外是可以理解的,但可恢復的bug就沒什
麼意義了。既然已經約定好了,你再違背,就屬於是bug而不是意外了(比如打
不開什麼的)。很多庫都不檢查指標的合法性(除了以外,因
為總不能讓系統dump core吧),也不檢查指標是否為NULL(因為如果層層都檢
查,必定勞民傷財,乾脆讓最上面呼叫的人在呼叫之外查)。
6、選擇d+f
選f+d, 好處如下:
a以最激烈的方式,充分暴露呼叫都的錯誤!能及時修改BUG
b便於,問題出現後,直接到事故現場。比120還快!
c對於realse版的程式碼沒有任何副作用。
d以處理的代價來看 採用斷言也是編寫最小一種。
e它是多語種,多平臺所通用的方式, 如:C /C++ VB,1.4 在win ,
通吃, 便於移置!
如果在現實中,測試沒有能找到所有的BUG,那可能就要用異常來幫忙了!
當然,我也提出了我的觀點, 我支援觀點6。理由如下:
assert只在debug標誌的時候有用,而在編譯release版本的時候不起作用。
assert對於檢查硬編碼的錯誤,是非常有用的,能夠及時的查處編碼的錯
誤。比如borland c++的類庫中就有很多這樣的assert。但是assert
不是萬能的,因為有很多錯誤的發生不是完全在編譯時發生的,而是執行
時的錯誤。在release後,assert是不可能依賴的。那麼,我們就需要
exception這一機制來檢測執行時錯誤,並相應的做出處理。當然,在異常
檢測和處理過程中還有許多需要討論的問題,由於不是這一題目的範圍,
我們沒有必要繼續討論得太多,但是,提出來希望大家注意:異常不是捕
獲了就完成任務了,而要對於不同的情況,採取不同的處理辦法,千萬不
能只是捕獲,而不做任何處理,那樣和不捕獲異常沒有任何區別。
在題目剛剛提出的時候,選擇各種答案的人都有,所以,我有必要在這裡把
其他答案為什麼不能選的理由說一下。
(a) if (!pParam)
return 0;
這是很多初級員常常採取的一種方式。返回值設為0。 因為函式的返回
值往往是計算的結果,不贊成把錯誤標誌值和計算結果混在一起使用,容易
造成使用者的誤會。當然,在很多unix函式中,由於歷史原因,還存在很多
這樣子的函式,所以需要指出,不要沿用這種方式。
(b) if (!pParam)
return ERROR_PARAM;
b比a稍微好一點點,返回了一個常量或者預定義的宏。 從返回值的字面上,
呼叫者能知道發生了什麼錯誤,但是,這也不是一種好的方法。
(c) if (!pParam)
pParam = "";
...
這是最不好的方式。直接給pParam賦予空字串,然後繼續函式過程,這
容易造成不可預料的後果,是程式不穩定的根源。
(d) if (!pParam)
throw EXCEPTION_ERROR_PARAM;
丟擲異常,剛剛已經討論過了,不再贅述。
(e) if (!pParam)
MessageBox(...);
這是一種比較可笑的方式,當然也有不少人用。MessageBox是直接彈出一個
對話方塊,告訴使用者,出錯了。但是並不做任何處理,程式繼續往下,
直到出錯崩潰。呵呵
(f) assert(!pParam);
斷言,剛剛已經討論過了,不再贅述。
以上這個題目,引發了所有與會者的興趣,討論異常熱烈,最後,主持人
也給出了自己的觀點:d+f。當然這並不是標準答案,因為這一門課程
本來就沒有什麼標準答案,大家見仁見智,這個答案只是的積累。
主持人緊接著列出了"編寫優質無錯程式碼的經驗":
a.理想的和實際的編譯器
b.使用斷言
c.函式的介面設計
d.考慮風險
e.態度的問題
以上是本節的主要內容。斷言,剛剛的問題中已經討論過了,來看看其他的
內容。
理想的編譯器和實際的編譯器:
題目二:
下面memcpy函式實現有什麼問題:
Void *memcpy(void *pvTo,void *pvFrom,size_t size){
byte *pbTo=(byte *) pvTo;
byte *pbFrom=(byte *)pvFrom;
while(size -- >0);
*pbTo++= *pbFrom++;
return pvTo;
}
呵呵,粗略一看,這函式還真找不出問題來。但是仔細看看,你就會發現
while(size -- >0);
這裡多了一個分號,導致下面的*pbTo++= *pbFrom++;不是在while迴圈中
執行多次,而是隻執行了一次。當然這不是函式設計者的預期結果,而編
譯器卻不會報告錯誤,因為while(size -- >0);從語法上來講,並沒有
錯誤。這就是理想的編譯器和實際的編譯器的區別所在。
那麼,該怎麼檢查這種錯誤呢?主持人提出瞭如下辦法:
while(size -- >0) NULL; 可以加入NULL來解決空語句. 這樣子,當你需
要 while(size -- >0)
*pbTo++= *pbFrom++;
這種形式的時候,就不會發生錯誤了,只需要用眼睛看看,就能發現了。
兩點好處 1 無冗餘程式碼,2 使人更明白。減少風險.
還有人會這麼寫
if( (n=read(....)) == 1) ....
在這裡,賦值符號=和判斷相等的符號==容易敲錯,而編譯器又檢查不出來,
可能就會有如下錯誤:
If(ch = ‘ ’)...;這也是需要注意的問題。
理想的編譯器和實際的編譯器小結:
a.把屢次出錯的合法的C習慣用法看成程式中的錯誤
b.增強編譯器的警告級別
c.使用其它的工具來檢查程式碼 如 Lint 等
d.進行單元測試
e.消除程式錯誤的最好方法是儘可能早、儘可能容易地發現錯誤,要尋求費力最小的自動查錯的方法
f.努力減少程式設計師查錯所需的技巧
使用斷言
題目三
下面函式實現,哪一個好,為什麼?
a.
char Uptolower(char ch){
if(ch >= ‘A’ && ch <= ‘Z’)
return ch+=‘a’-’A’;
return -1;
}
b.
char Uptolower(char ch){
assert(ch >= ‘A’ && ch <= ‘Z’);
if(ch >= ‘A’ && ch <= ‘Z’)
return ch+=‘a’-’A’;
return ch;
}
c.
char Uptolower(char ch){
assert(ch >= ‘A’ && ch <= ‘Z’);
return ch+(‘a’-’A’);
}
分析:
a.該函式檢查ch是否在A..Z之間,如果是,則返回相應的小寫字元,如果
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-998418/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 🐒編寫高質量程式碼(手撕程式碼)
- 如何寫出更優質的程式碼
- 如何提高Java程式碼質量-優雅的寫程式碼Java
- Java程式碼編寫、程式碼優化技巧總結Java優化
- 編寫更優雅的 JavaScript 程式碼JavaScript
- 消除程式碼中的壞味道,編寫高質量程式碼
- iOS 編寫高質量Objective-C程式碼iOSObjectC程式
- java程式碼編寫優化(持續更新...)Java優化
- 編寫優雅程式碼的最佳實踐
- JavaScript 如何工作: 深入 V8 引擎 + 編寫優質程式碼的 5 個技巧JavaScript
- 如何寫出優質乾淨的程式碼,這6個技巧你不能錯過!
- iOS編寫高質量Objective-C程式碼(六)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(七)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(八)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(六)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(五)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(一)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(二)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(四)iOSObjectC程式
- iOS編寫高質量Objective-C程式碼(四)iOSObjectC程式
- iOS編寫高質量Objective-C程式碼(二)iOSObjectC程式
- iOS 編寫高質量Objective-C程式碼(三)iOSObjectC程式
- 如何編寫高質量的C#程式碼(一)C#
- 編寫高質量程式碼的十個祕訣
- golang如何優雅的編寫事務程式碼Golang
- 編寫高質量程式碼 改善Python程式的91個建議Python
- 編寫 SQL 程式碼時常犯的九個錯誤SQL
- 幽默:編寫Python程式碼你們使用什麼偵錯程式?Python
- 什麼程式碼是ChatGPT無法編寫的? - datachimpChatGPT
- iOS 編寫高質量Objective-C程式碼(一)—— 簡介iOSObjectC程式
- 我們應該如何編寫高質量的前端程式碼前端
- 無需編寫程式碼,API業務流程測試,零程式碼實現API
- 低程式碼優於無程式碼?
- VSCode使用技巧,程式碼編寫效率提升2倍以上!VSCode
- Sublime 編寫編譯 swift程式碼編譯Swift
- 《編寫高質量程式碼:改善Java程式的151個建議》筆記Java筆記
- 《Effective JavaScript 編寫高質量JavaScript程式碼的68個有效方法》JavaScript
- 如何編寫優秀的測試程式碼|單元測試
- 編寫靈活、穩定、高質量的HTML程式碼的規範HTML