程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 編程精粹--編寫高質量C語言代碼(1):假想編譯程序

編程精粹--編寫高質量C語言代碼(1):假想編譯程序

編輯:C++入門知識

編譯程序僅僅能查找出程序的語法錯誤,而對於“數組越界訪問”,“對空指針解引用”等錯誤,編譯程序是束手無策的。同時我們知道測試人員所使用的黑箱測試方法所能做的只是往程序裡填數據,並看它彈出什麼。這就決定了對程序錯誤的檢測可能需要點運氣。

假如編譯程序能夠檢測出“數組越界訪問”,“差一錯誤”,“空指針”等等錯誤,那麼編寫無錯代碼其實就要簡答多了。

所以我們需要一個思維轉變: 不要光依賴黑箱測試方法,還應該試著去模仿前面所講的假想編譯程序,來排除運氣對程序測試的影響,自動地抓住錯誤的每個機會。

好的編譯程序應該能夠這樣: 可以把屢次出錯的合法的C習慣用法看成程序中的錯誤。這句話什麼意思呢? 一些C用法從語法上講是合法的,但是往往卻給程序帶來意想不到的錯誤。所以好的編譯程序應該提供支持:讓我們把這些用法當成錯誤。

舉個例子:

/* 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條件判斷語句之後多了一個分號,這導致循環體為空語句。這顯然不是程序員的意圖,但是編譯器卻無法檢測出這個錯誤,因為空語句從語法角度上是合法的。

盡管在C語言中空語句本身是合法的,但是我們的確很少這樣使用,出現空語句時往往是由於程序員不小心導致的,而這樣的空語句也會導致隱藏很深的錯誤。所以當出現空語句時,如果編譯器把它認為是個錯誤,並自動給我們一個警告,這樣讓我們非常容易查找出錯誤。

當然如果我們的確要使用空語句時,那就用。但是最好使用NULL使其明顯可見。NULL只是個常量,所以編譯程序不會為NULL語句生成任何代碼。這樣,編譯程序只接受顯示的NULL語句,而把隱式的空語句(即只有一個分號)標示為錯誤。這就使得我們既可以明確地使用空語句,同時又可以指示出那些隱式的往往導致錯誤的空語句。

還有一種常見的問題就是無意的賦值。例如

if(ch=‘\t')
    ExpandTab();

我們是想判斷ch是否和‘\t’相等,卻導致‘\t’賦值給ch,這導致程序的行為和我們期望的大相徑庭。但是編譯器卻不會給出任何抱怨,因為這是合法的C語句。所以某些編譯程序允許用戶在&& 和 || 表達式以及if,for, while構造的表達式中禁止使用簡單賦值。這樣就可以幫助用戶查出這種錯誤。這樣做的基本依據就是用戶極有可能在以上五種情況下把“==”打成“=”。

但是有時為了代碼的簡單性,我們可能編寫出以下代碼

while(*pchTo++=*pchFrom++)
    NULL;

所以此時為了避免警告信息,我們可以這樣編寫

while((*pchTp++=*pchFrom)!='\0')
    NULL;

這樣做盡管看上去要麻煩,但是現代的商業級編譯器不會為這種的冗余代碼產生額外的代碼,會把它優化掉。同時又可以減少風險,更加安全。

空語句,錯誤的賦值以及原型檢查等只是許多C編譯程序提供的選擇項中的一小部分內容,實際上還有更多的選擇項。這裡的要點是:用戶可以選擇的編譯程序警告設施可以就可能的錯誤向用戶發出警告信息。盡管有時為了這些警告設施,我們可能需要一些額外的工作,但是我們應該把這些警告設施看成一種無風險高償還的程序投資。

使用編譯程序所有的可選警告設施。

另一種檢查錯誤更詳細,更徹底的方法是使用lint。lint這個工具最初是用來掃描C源文件並對源程序中不可移植的部分提出警告,現在的lint實用程序變得更加嚴謹,lint可以檢測出雖然可移植並且完全合乎語法但很有可能是錯誤的特性。


使用lint來檢查出編譯程序漏掉的錯誤。


有時,似乎可以跳過一些設計用來避免程序出錯的步驟,例如單元測試,但是走捷徑之時,就是麻煩將至之日。

如果有單元測試,就進行單元測試。

總結: 當你寫程序時,要在心中時刻牢記著假想編譯程序這一概念,這樣就可以花費很少力氣利用每個機會抓住錯誤。要考慮編譯程序產生的錯誤,lint產生的錯誤以及單元測試失敗的原因。消除程序錯誤的最好方法是盡可能早,盡可能容易地發現錯誤,要尋求費力最小的自動差錯方法。

最後用作者在本章裡的一句引言結束這篇文章:

投資者與賭徒之間的區別在於投資者利用每一次機會,無論它是多麼小,去爭取利益;而賭徒只靠運氣。




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved