BUG,規范,斷言和調試(2)

發表于:2011-11-04來源:未知作者:領測軟件測試網采編點擊數: 標簽:Bug管理
編譯器不能捕獲這種運行時才會出現的錯誤(順便說一下,我在CSDN上居然還看到有人抱怨編譯器不會報告除0錯誤);也不能捕獲算法中的BUG和檢驗參數中的數

  編譯器不能捕獲這種運行時才會出現的錯誤(順便說一下,我在CSDN上居然還看到有人抱怨編譯器不會報告除0錯誤);也不能捕獲算法中的BUG和檢驗參數中的數據。但要是你知道怎么做的話,這類問題很容易被發現。你可以用SetProcessWorkingSetSize函數或者msconfig工具減少虛擬內存大小,或者使用Virtual PC之類的虛擬機或者磁盤配額策略來模擬內存和磁盤空間不足的情況。

  你有可能想在這種極限情況下調試你的代碼,但是大多數時間內,內存分配不會失敗,而設置條件斷點又太麻煩了。這時候可以在代碼里面加上一段用來在內存分配失敗時觸發調試器的斷言代碼

  void MyZeroMemory(char* strBuffer, int length)...{ assert(strBuffer!=NULL);}

  如果使用的是MFC或者ATL,建議使用對應的宏ASSERT和ATLASSERT?,F在你可以編寫健壯的代碼使得程序在strBuffer這塊內存分配失敗時也能夠正常運行。

  現在的問題是,加入的這些代碼增加了應用程序的大小,減慢了運行速度。在解決了內存分配失敗造成的程序崩潰的問題之后,有必要在發布的版本中去掉這些斷言代碼。一個簡單的辦法是使用預處理標識符:

  void MyZeroMemory(char* strBuffer, int length)...{#ifdef DEBUG assert(strBuffer!=NULL);#endif}

  這樣你可以只維護同一份代碼。當然,這也意味著調試的代碼在發行版中會被去除,所以為了避免不可預料的行為,為了調試而加入的代碼應該盡可能少地影響應用程序的行為。

  你有可能需要重新定義assert來實現擴展的行為——例如在assert斷言失敗中斷程序時打開源文件并且跳到assert那一行——這時候你可以編寫自己的斷言函數,然后重定義assert為這個斷言函數。

  #ifdef DEBUG

  /**//*display a dialog and if the user selected break

  , jump to the assert line*/

  void _assert(char*,unsigned int);

  #define assert(f)\

  if(f)\

  ...{}\

  else

  _assert(__FILE__,__LINE__)

  #else

  #define assert(f)

  #endif

  空的if語句塊可能看起來有點奇怪,但是這可以避免和宏外的if-else產生沖突。同樣,最后一行語句沒有結束的分號,因為在使用的時候再加上會更加自然。

  assert最有用的地方就是用來檢驗函數的參數——但是也可以在其他地方起作用。在程序中的斷言語句越多,異常的情況就越容易被偵測到。

  既然assert是代碼,它不可避免的需要注釋。即使是自己寫的代碼,過了六個月之后再來審視也可能需要一點時間來重新理解這部分代碼。一個簡單的注釋可以把這部分時間減少:

  void MyZeroMemory(char* strBuffer, int length)

  ...{

  /**//*should not be called when buffer allocation failed*/

  #ifdef DEBUG

  assert(strBuffer!=NULL);

  #endif

  }

  在編寫完函數之后,應該審視函數中的代碼,之后在函數的開頭驗證函數正常運行所需的條件。如果你在寫一個庫函數,那么應該在函數的文檔中加入函數正常運行所需的條件——否則就會增加使用者發現BUG的難度。舉例來說,Windows API的文檔不可謂不詳盡,但是我在用匯編調用Windows API的時候,也花了很長時間才發現調用Windows API之前棧頂要設置成4的倍數。

  注意不要把一些條件當成默認成立的了。assert(sizeof(int)==4);這樣的語句在一些人看來很荒謬,但是在Windows開發中通常是32位的long在一些64位平臺上已經是64位的了,而在目前還不知道sizeof(int)在什么時候會升位。如果你的代碼依賴于int的大小,那么寫上這行可以在未來升位之后更快發現問題。

  一些保守的程序員在參數錯誤時會讓函數繼續運行——返回一個錯誤碼——但是不報告錯誤。在編寫核心模塊時這可能很有必要,但是這也經常會把BUG藏起來——在多層函數返回之后時候,錯誤碼經常會丟失或者被替代。盡量不要使用保守編程來替代斷言,如果你認為保守編程會造成定位問題的困難,那么就加上斷言代碼。

  在一些時候,校驗參數數據似乎是不可能的事情——想象一下那些被設計來搞糊涂解密者的加密算法的中間數據——但是校驗這種復雜算法的方法也不是沒有。為了確認手算和心算的正確性,我們會使用電子計算器的結果來進行比較,反過來,我們也可以編寫另外一個的算法來斷言計算結果的正確性。這種方法也可以被用來斷言一個函數的匯編版本和C版本的一致性——為了獲取最大性能,函數的匯編版本的算法可能和C版本的有很大差異。當然,不是每個函數都有必要用這種方式來驗證,實際上,只有極其重要的算法和對性能極其敏感的代碼才會需要這種雙保險來驗證。同樣,為了調試而加入的算法也應該盡可能少地影響應用程序的行為。

  最后,你不應該在發布程序時從代碼中去掉斷言語句,而是把它們留在那里以供你升級或者查找BUG時使用。帖來自于網易社區:http://p5.club.163.com/viewArticleByWWW.m?boardId=clanguage&articleId=clanguage_10938d0575c4afe

  消滅不可預料的行為

  斷言并不能抓住所有BUG——它們都是人寫出來的,而是人就會犯錯誤。一些常見的錯誤包括:

  使用狀態不確定的資源

  在釋放資源之后繼續訪問資源

  在資源重新定位之后繼續引用舊的資源

  申請資源之后丟失對資源的引用

  訪問時未注意是否越界

  忽略錯誤信息

  這些并不是杞人憂天的問題——實際上,這些問題屬于日常開發中最常見的問題。這些問題的特點是,它們并不是時常造成程序行為的異常,并且癥狀不可重復。以內存為例,釋放內存之后編譯器和操作系統通常不會自動去擦除內存中的內容,所以繼續訪問內存不太可能造成程序行為的異常——直到內存被重新分配出去,而另一塊代碼開始重寫這塊數據。申請資源之后丟失對資源的引用可能只是造成長時間運行之后系統資源不足而已。另外,這些問題都是算法的問題,而編譯器并不會替你校驗你的算法,你自己也不太可能會懷疑你自己的算法。

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97