天天看點

代碼防禦性程式設計的十條技巧

1 什麼是防禦性程式設計?

顧名思義,防禦性程式設計是一種細緻、謹慎的程式設計方法。為了開發可靠的軟體,我們要設計系統中的每個元件,以使其盡可能的”保護”自己。我們通過明确地在代碼中對設想進行檢查,這是一種努力,防止我們的代碼以将會展現錯誤行為的方式被調用。

防禦性程式設計使我們可以盡早的發現較小的問題,而不是等到它們發展成大的災難的時候才發現。其開發軟體的過程是:

代碼防禦性程式設計的十條技巧

下面總結了一些防禦性程式設計的反對和支援者的意見:

反對者:

它降低了代碼的效;即使是一個很小的額外代碼也需要一些額外的執行時間。它對于一個函數來說也許不要緊,但是對于一個由10萬個函數組成的系統,問題就變得嚴重了。

每種防禦性的做法都需要一些額外的工作;

支援者:

防禦性程式設計可以節省大量的調試時間,使你可以去做更有意義的事情。

編寫可以正常運作、隻是速度有些慢的代碼,要遠遠好過大多數時間都正常運作、但是有時候會崩潰的代碼。

防禦性程式設計避免了大量的安全性問題。

防禦性程式設計技巧

使用好的編碼風格和合理的設計

采用良好的編碼風格,來防範大多數編碼錯誤。如:

const關鍵字:

關鍵字const可以給讀你代碼的人傳達非常有用的資訊。例如,在函數的形參前添加const關鍵字意味着這個參數在函數體内不會被修改,屬于輸入參數。

同時,合理地使用關鍵字const可以使編譯器很自然的保護那些不希望被修改的參數,防止其被無意的代碼修改,減少bug的出現。

volatile關鍵字:

在一些并行裝置的硬體寄存器(如狀态寄存器),中斷服務子程式中會通路到的全局變量以及多線程應用中被幾個任務共享的變量前使用volatile關鍵字來防止編譯優化。

static關鍵字:

函數體内static變量的作用範圍為該函數體,不同于auto變量,該變量的記憶體隻被配置設定一次,是以其值在下次調用時仍維持上次的值。

在子產品内的static全局變量可以被子產品内的所有函數通路,但不能被子產品外其它函數通路。

在子產品内的static函數隻可能被這一子產品内的其它函數調用,這個函數的使用範圍被限制在聲明它的子產品内。

位操作運算中,盡可能使用<<、 >>、 &、|等運算符,盡可能少使用/、%、*運算符。

變量和函數的命名要有意義,并且盡可能做到一個函數隻做一件事情。

多采用面向對象的思想來編寫代碼。

在投入到編碼工作之前,先考慮大體的設計方案,這也非常關鍵。

不要倉促的編寫代碼

欲速則不達,每敲一個字,都要想清楚你要輸入的是什麼。在寫每一行時都三思而後行。可能會出現什麼樣的錯誤?你是否已經考慮了所有可能出現的邏輯分支?放慢速度,有條不紊的程式設計雖然看上去很平凡,但這的确是減少缺陷的好辦法。

如C語言程式設計中,追求速度的程式員經常會出現的一個問題就是将”==”錯誤的輸入為”=”,而有些編譯器并不會警告,這就會造成問題。

不要相信任何人

這裡是指用懷疑的眼光來審視所有的輸入和所有的結果,直到你能證明這段代碼是正确的時候為止。

編碼的目标要清晰,而不是簡潔

簡單是一種美,不要讓你的代碼過于複雜。即編寫的代碼一定要邏輯清晰,可讀性強。

編譯時打開所有警告開關

在你的代碼中産生任何警告資訊,都應立即修正代碼。要知道警告的出現總是有原因的。即使你認為某個警告無關緊要,也不要置之不理。

使用安全的資料結構

我們最常見的一些安全隐患大概是由緩沖溢出引起的。緩沖溢出是由于不正确的使用固定大小的資料結構而造成的。例如,如下這個代碼:

char * unsafe_copy(const char * source)
{
   char *buffer = new char[10];
   strcpy(buffer,source);
   return buffer;
}      

如果source中的資料長度超過10個字元,它就會造成其它問題。我們可以改成如下形式:

char * safe_copy(const char * source)
{
   char *buffer = new char[10];
   strncpy(buffer,source,10);  //用strncpy代替strcpy可以保護這個代碼段
   return buffer;
}      

檢查所有的傳回值

如果一個函數傳回一個值,他這樣做肯定是有理由的。檢查這個傳回值,如果傳回值是一個錯誤代碼,你就必須辨識這個代碼并處理所有的錯誤。不要讓錯誤悄無聲息的侵入你的程式;大多數難以察覺的錯誤都是因為程式員沒有檢查傳回值而出現的。

審慎的處理記憶體

對于在執行期間所擷取的任何資源,必須徹底釋放。

在聲明位置初始化所有變量

如果你意外的使用了一個沒有初始化的變量,那麼你的程式在每次運作的時候都将得到不同的結果,這取決于當時記憶體中的垃圾資訊是什麼。這樣會造成很多随機的行為,給查找帶來很多的麻煩。是以,需要在聲明每個變量的時候就對它進行初始化。

同時,平時編碼時還要注意一些細則

提供預設的行為:Switch語句中将default case的執行明示出來。同樣地,如果你要編寫一些不帶else子句的if語句,停下來想一想,你是否該處理這個邏輯上的預設情況

檢查數值的上下限:確定每次運算數值變量都不會溢出,即資料類型的使用要謹慎

注意強制轉換是否合理

聲明變量,可以使變量的聲明位置與使用它的位置盡量接近,進而防止它幹擾代碼的其他部分

加合理的異常處理、日志檔案

正确設定常量

優秀的程式應該做到:

關心代碼是否健壯

確定每個設想都顯示地展現在防禦性代碼中

希望代碼對無用資訊的輸入有正确的行為

在程式設計的時候認真思考自己所編寫的代碼

編寫可以保護自己不受其他人的愚蠢傷害的代碼。

繼續閱讀