華爲C語言編程規範(9)—質量保證

8-1:在軟件設計過程中構築軟件質量。

8-2:代碼質量保證優先原則
(1)正確性,指程序要實現設計要求的功能。
(2)穩定性、安全性,指程序穩定、可靠、安全。
(3)可測試性,指程序要具有良好的可測試性。
(4)規範/可讀性,指程序書寫風格、命名規則等要符合規範。
(5)全局效率,指軟件系統的整體效率。
(6)局部效率,指某個模塊/子模塊/函數的本身效率。
(7)個人表達方式/個人方便性,指個人編程習慣。

8-3:只引用屬於自己的存貯空間。

說明:若模塊封裝的較好,那麼一般不會發生非法引用他人的空間。

8-4:防止引用已經釋放的內存空間。

說明:在實際編程過程中,稍不留心就會出現在一個模塊中釋放了某個內存塊(如C 語言指針),而另一模塊在隨後的某個時刻又使用了它。要防止這種情況發生。

8-5:過程/函數中分配的內存,在過程/函數退出之前要釋放。

8-6:過程/函數中申請的(爲打開文件而使用的)文件句柄,在過程/函數退出之前要關閉。

說明:分配的內存不釋放以及文件句柄不關閉,是較常見的錯誤,而且稍不注意就有可能發生。這類錯誤往往會引起很嚴重後果,且難以定位。

示例:下函數在退出之前,沒有把分配的內存釋放。
typedef unsigned char BYTE;
int example_fun( BYTE gt_len, BYTE *gt_code )
{
BYTE *gt_buf;
gt_buf = (BYTE *) malloc (MAX_GT_LENGTH);
... //program code, include check gt_buf if or not NULL.
/* global title length error */
if (gt_len > MAX_GT_LENGTH)
{
return GT_LENGTH_ERROR; // 忘了釋放gt_buf
}
... // other program code
}

應改爲如下。
int example_fun( BYTE gt_len, BYTE *gt_code )
{
BYTE *gt_buf;
gt_buf = (BYTE * ) malloc ( MAX_GT_LENGTH );
... // program code, include check gt_buf if or not NULL.
/* global title length error */
if (gt_len > MAX_GT_LENGTH)
{
free( gt_buf ); // 退出之前釋放gt_buf
return GT_LENGTH_ERROR;
}
... // other program code
}

8-7:防止內存操作越界。

說明:內存操作主要是指對數組、指針、內存地址等的操作。內存操作越界是軟件系統主要錯誤之一,後果往往非常嚴重,所以當我們進行這些操作時一定要仔細小心。

示例:假設某軟件系統最多可由10 個用戶同時使用,用戶號爲1-10,那麼如下程序存在問題。
#define MAX_USR_NUM 10
unsigned char usr_login_flg[MAX_USR_NUM]= "";
void set_usr_login_flg( unsigned char usr_no )
{
if (!usr_login_flg[usr_no])
{
usr_login_flg[usr_no]= TRUE;
}
}

當usr_no 爲10 時,將使用usr_login_flg 越界。可採用如下方式解決。
void set_usr_login_flg( unsigned char usr_no )
{
if (!usr_login_flg[usr_no - 1])
{
usr_login_flg[usr_no - 1]= TRUE;
}
}

8-8:認真處理程序所能遇到的各種出錯情況。

8-9:系統運行之初,要初始化有關變量及運行環境,防止未經初始化的變量被引用。

8-10:系統運行之初,要對加載到系統中的數據進行一致性檢查。

說明:使用不一致的數據,容易使系統進入混亂狀態和不可知狀態。

8-11:嚴禁隨意更改其它模塊或系統的有關設置和配置。

說明:編程時,不能隨心所欲地更改不屬於自己模塊的有關設置如常量、數組的大小等。

8-12:不能隨意改變與其它模塊的接口。

8-13:充分了解系統的接口之後,再使用系統提供的功能。

示例:在B 型機的各模塊與操作系統的接口函數中,有一個要由各模塊負責編寫的初始化過程,此過程在軟件系統加載完成後,由操作系統發送的初始化消息來調度。因此就涉及到初始化消息的類型與消息發送的順序問題,特別是消息順序,若沒搞清楚就開始編程,很容易引起嚴重後果。以下示例引自B 型曾出現過的實際代碼,其中使用了FID_FETCH_DATA與FID_INITIAL 初始化消息類型,注意B 型機的系統是在FID_FETCH_DATA 之前發送FID_INITIAL 的。

MID alarm_module_list[MAX_ALARM_MID];
int FAR SYS_ALARM_proc( FID function_id, int handle )
{
_UI i, j;
switch ( function_id )
{
... // program code
case FID_INITAIL:
for (i = 0; i < MAX_ALARM_MID; i++)
{
if (alarm_module_list[i]== BAM_MODULE // **)
|| (alarm_module_list[i]== LOCAL_MODULE)
{
for (j = 0; j < ALARM_CLASS_SUM; j++)
{
FAR_MALLOC( ... );
}
}
}
... // program code
break;
case FID_FETCH_DATA:
... // program code
Get_Alarm_Module( ); // 初始化alarm_module_list
break;
... // program code
}
}
由於FID_INITIAL 是在FID_FETCH_DATA 之前執行的,而初始化alarm_module_list 是在FID_FETCH_DATA 中進行的,故在FID_INITIAL 中(**)處引用alarm_module_list 變量時,它還沒有被初始化。這是個嚴重錯誤。應如下改正:要麼把Get_Alarm_Module 函數放在FID_INITIAL 中(**)之前;要麼就必須考慮(**)處的判斷語句是否可以用(不使用alarm_module_list 變量的)其它方式替代,或者是否可以取消此判斷語句。

8-14:編程時,要防止差1 錯誤。

說明:此類錯誤一般是由於把“<=”誤寫成“<”或“>=”誤寫成“>”等造成的,由此引起的後果,很多情況下是很嚴重的,所以編程時,一定要在這些地方小心。當編完程序後,應對這些操作符進行徹底檢查。

8-15:要時刻注意易混淆的操作符。當編完程序後,應從頭至尾檢查一遍這些操作符,以防止拼寫錯誤。

說明:形式相近的操作符最容易引起誤用,如C/C++中的“=”與“==”、“|”與“||”、“&”與“&&”等,若拼寫錯了,編譯器不一定能夠檢查出來。

示例:如把“&”寫成“&&”,或反之。
ret_flg = (pmsg->ret_flg & RETURN_MASK);
被寫爲:
ret_flg = (pmsg->ret_flg && RETURN_MASK);
rpt_flg = (VALID_TASK_NO( taskno ) && DATA_NOT_ZERO( stat_data ));
被寫爲:
rpt_flg = (VALID_TASK_NO( taskno ) & DATA_NOT_ZERO( stat_data ));

8-16:有可能的話,if 語句儘量加上else 分支,對沒有else 分支的語句要小心對待;switch語句必須有default 分支。

8-17:Unix 下,多線程的中的子線程退出必需採用主動退出方式,即子線程應return 出口。

8-18:不要濫用goto 語句。

說明:goto 語句會破壞程序的結構性,所以除非確實需要,最好不使用goto 語句。

8-19:精心地構造、劃分子模塊,並按“接口”部分及“內核”部分合理地組織子模塊,以提高“內核”部分的可移植性和可重用性。

說明:對不同產品中的某個功能相同的模塊,若能做到其內核部分完全或基本一致,那麼無論對產品的測試、維護,還是對以後產品的升級都會有很大幫助。

8-20:精心構造算法,並對其性能、效率進行測試。

8-21:對較關鍵的算法最好使用其它算法來確認。

8-22:時刻注意表達式是否會上溢、下溢。

示例:如下程序將造成變量下溢。
unsigned char size ;
while (size-- >= 0) // 將出現下溢
{
... // program code
}
當size 等於0 時,再減1 不會小於0,而是0xFF,故程序是一個死循環。應如下修改。
char size; // 從unsigned char 改爲char
while (size-- >= 0)
{
... // program code
}

8-23:使用變量時要注意其邊界值的情況。

示例:如C 語言中字符型變量,有效值範圍爲-128 到127。故以下表達式的計算存在一定風險。
char chr = 127;
int sum = 200;
chr += 1; // 127 爲chr 的邊界值,再加1 將使chr 上溢到-128,而不是128。
sum += chr; // 故sum 的結果不是328,而是72。
若chr 與sum 爲同一種類型,或表達式按如下方式書寫,可能會好些。
sum = sum + chr + 1;

8-24:留心程序機器碼大小(如指令空間大小、數據空間大小、堆棧空間大小等)是否超出系統有關限制。

8-25:爲用戶提供良好的接口界面,使用戶能較充分地瞭解系統內部運行狀態及有關係統出錯情況。

8-26:系統應具有一定的容錯能力,對一些錯誤事件(如用戶誤操作等)能進行自動補救。

8-27:對一些具有危險性的操作代碼(如寫硬盤、刪數據等)要仔細考慮,防止對數據、硬件等的安全構成危害,以提高系統的安全性。

8-28:使用第三方提供的軟件開發工具包或控件時,要注意以下幾點:
(1)充分了解應用接口、使用環境及使用時注意事項。
(2)不能過分相信其正確性。
(3)除非必要,不要使用不熟悉的第三方工具包與控件。
說明:使用工具包與控件,可加快程序開發速度,節省時間,但使用之前一定對它有較充分的瞭解,同時第三方工具包與控件也有可能存在問題。

8-29:資源文件(多語言版本支持),如果資源是對語言敏感的,應讓該資源與源代碼文件脫離,具體方法有下面幾種:使用單獨的資源文件、DLL 文件或其它單獨的描述文件(如數據庫格式)

9-1:打開編譯器的所有告警開關對程序進行編譯。

9-2:在產品軟件(項目組)中,要統一編譯開關選項。

9-3:通過代碼走讀及審查方式對代碼進行檢查。

說明:代碼走讀主要是對程序的編程風格如註釋、命名等以及編程時易出錯的內容進行檢查,可由開發人員自己或開發人員交叉的方式進行;代碼審查主要是對程序實現的功能及程序的穩定性、安全性、可靠性等進行檢查及評審,可通過自審、交叉審覈或指定部門抽查等方式進行。

9-4:測試部測試產品之前,應對代碼進行抽查及評審。

9-5:編寫代碼時要注意隨時保存,並定期備份,防止由於斷電、硬盤損壞等原因造成代碼丟失。

9-6:同產品軟件(項目組)內,最好使用相同的編輯器,並使用相同的設置選項。

說明:同一項目組最好採用相同的智能語言編輯器,如Muiti Editor,Visual Editor 等,並設計、使用一套縮進宏及註釋宏等,將縮進等問題交由編輯器處理。

9-7:合理地設計軟件系統目錄,方便開發人員使用。

說明:方便、合理的軟件系統目錄,可提高工作效率。目錄構造的原則是方便有關源程序的存儲、查詢、編譯、鏈接等工作,同時目錄中還應具有工作目錄----所有的編譯、鏈接等工作應在此目錄中進行,工具目錄----有關文件編輯器、文件查找等工具可存放在此目錄中。

9-8:某些語句經編譯後產生告警,但如果你認爲它是正確的,那麼應通過某種手段去掉告警信息。

說明:在Borland C/C++中,可用“#pragma warn”來關掉或打開某些告警。

示例:
#pragma warn -rvl // 關閉告警
int examples_fun( void )
{
// 程序,但無return 語句。
}
#pragma warn +rvl // 打開告警
編譯函數examples_fun 時本應產生“函數應有返回值”告警,但由於關掉了此告警信息顯
示,所以編譯時將不會產生此告警提示。

9-9:使用代碼檢查工具(如C 語言用PC-Lint)對源程序檢查。

9-10:使用軟件工具(如 LogiSCOPE)進行代碼審查。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章