?? 編程規范與范例(4).txt
字號:
[編程技巧]編程規范與范例(4)
程序匠人 發表于 2005-8-27 20:17:00 閱讀全文(196) | 回復(0) | 引用(0)
編程規范與范例(4)
〖文章轉載或出處〗≡中國電子技術信息網≡ 網址:www.CETINet.com
編程規范與范例(4)
目 錄
1 排版 6
2 注釋 11
3 標識符命名 18
4 可讀性 20
5 變量、結構 22
6 函數、過程 28
7 可測性 36
8 程序效率 40
9 質量保證 44
10 代碼編輯、編譯、審查 50
11 代碼測試、維護 52
12 宏 53
7 可測性
¹ 7-1:在同一項目組或產品組內,要有一套統一的為集成測試與系統聯調準備的調測開關及相應打印函數,并且要有詳細的說明。
說明:本規則是針對項目組或產品組的。
¹ 7-2:在同一項目組或產品組內,調測打印出的信息串的格式要有統一的形式。信息串中至少要有所在模塊名(或源文件名)及行號。
說明:統一的調測信息格式便于集成測試。
¹ 7-3:編程的同時要為單元測試選擇恰當的測試點,并仔細構造測試代碼、測試用例,同時給出明確的注釋說明。測試代碼部分應作為(模塊中的)一個子模塊,以方便測試代碼在模塊中的安裝與拆卸(通過調測開關)。
說明:為單元測試而準備。
¹ 7-4:在進行集成測試/系統聯調之前,要構造好測試環境、測試項目及測試用例,同時仔細分析并優化測試用例,以提高測試效率。
說明:好的測試用例應盡可能模擬出程序所遇到的邊界值、各種復雜環境及一些極端情況等。
¹ 7-5:使用斷言來發現軟件問題,提高代碼可測性。
說明:斷言是對某種假設條件進行檢查(可理解為若條件成立則無動作,否則應報告),它可以快速發現并定位軟件問題,同時對系統錯誤進行自動報警。斷言可以對在系統中隱藏很深,用其它手段極難發現的問題進行定位,從而縮短軟件問題定位時間,提高系統的可測性。實際應用時,可根據具體情況靈活地設計斷言。
示例:下面是C語言中的一個斷言,用宏來設計的。(其中NULL為0L)
#ifdef _EXAM_ASSERT_TEST_ // 若使用斷言測試
void exam_assert( char * file_name, unsigned int line_no )
{
printf( "\n[EXAM]Assert failed: %s, line %u\n",
file_name, line_no );
abort( );
}
#define EXAM_ASSERT( condition )
if (condition) // 若條件成立,則無動作
NULL;
else // 否則報告
exam_assert( __FILE__, __LINE__ )
#else // 若不使用斷言測試
#define EXAM_ASSERT(condition) NULL
#endif /* end of ASSERT */
¹ 7-6:用斷言來檢查程序正常運行時不應發生但在調測時有可能發生的非法情況。
¹ 7-7:不能用斷言來檢查最終產品肯定會出現且必須處理的錯誤情況。
說明:斷言是用來處理不應該發生的錯誤情況的,對于可能會發生的且必須處理的情況要寫防錯程序,而不是斷言。如某模塊收到其它模塊或鏈路上的消息后,要對消息的合理性進行檢查,此過程為正常的錯誤檢查,不能用斷言來實現。
¹ 7-8:對較復雜的斷言加上明確的注釋。
說明:為復雜的斷言加注釋,可澄清斷言含義并減少不必要的誤用。
¹ 7-9:用斷言確認函數的參數。
示例:假設某函數參數中有一個指針,那么使用指針前可對它檢查,如下。
int exam_fun( unsigned char *str )
{
EXAM_ASSERT( str != NULL ); // 用斷言檢查“假設指針不為空”這個條件
... //other program code
}
¹ 7-10:用斷言保證沒有定義的特性或功能不被使用。
示例:假設某通信模塊在設計時,準備提供“無連接”和“連接” 這兩種業務。但當前的版本中僅實現了“無連接”業務,且在此版本的正式發行版中,用戶(上層模塊)不應產生“連接”業務的請求,那么在測試時可用斷言檢查用戶是否使用“連接”業務。如下。
#define EXAM_CONNECTIONLESS 0 // 無連接業務
#define EXAM_CONNECTION 1 // 連接業務
int msg_process( EXAM_MESSAGE *msg )
{
unsigned char service; /* message service class */
EXAM_ASSERT( msg != NULL );
service = get_msg_service_class( msg );
EXAM_ASSERT( service != EXAM_CONNECTION ); // 假設不使用連接業務
... //other program code
}
¹ 7-11:用斷言對程序開發環境(OS/Compiler/Hardware)的假設進行檢查。
說明:程序運行時所需的軟硬件環境及配置要求,不能用斷言來檢查,而必須由一段專門代碼處理。用斷言僅可對程序開發環境中的假設及所配置的某版本軟硬件是否具有某種功能的假設進行檢查。如某網卡是否在系統運行環境中配置了,應由程序中正式代碼來檢查;而此網卡是否具有某設想的功能,則可由斷言來檢查。
對編譯器提供的功能及特性假設可用斷言檢查,原因是軟件最終產品(即運行代碼或機器碼)與編譯器已沒有任何直接關系,即軟件運行過程中(注意不是編譯過程中)不會也不應該對編譯器的功能提出任何需求。
示例:用斷言檢查編譯器的int型數據占用的內存空間是否為2,如下。
EXAM_ASSERT( sizeof( int ) == 2 );
¹ 7-12:正式軟件產品中應把斷言及其它調測代碼去掉(即把有關的調測開關關掉)。
說明:加快軟件運行速度。
¹ 7-13:在軟件系統中設置與取消有關測試手段,不能對軟件實現的功能等產生影響。
說明:即有測試代碼的軟件和關掉測試代碼的軟件,在功能行為上應一致。
¹ 7-14:用調測開關來切換軟件的DEBUG版和正式版,而不要同時存在正式版本和DEBUG版本的不同源文件,以減少維護的難度。
¹ 7-15:軟件的DEBUG版本和發行版本應該統一維護,不允許分家,并且要時刻注意保證兩個版本在實現功能上的一致性。
½ 7-1:在編寫代碼之前,應預先設計好程序調試與測試的方法和手段,并設計好各種調測開關及相應測試代碼如打印函數等。
說明:程序的調試與測試是軟件生存周期中很重要的一個階段,如何對軟件進行較全面、高率的測試并盡可能地找出軟件中的錯誤就成為很關鍵的問題。因此在編寫源代碼之前,除了要有一套比較完善的測試計劃外,還應設計出一系列代碼測試手段,為單元測試、集成測試及系統聯調提供方便。
½ 7-2:調測開關應分為不同級別和類型。
說明:調測開關的設置及分類應從以下幾方面考慮:針對模塊或系統某部分代碼的調測;針對模塊或系統某功能的調測;出于某種其它目的,如對性能、容量等的測試。這樣做便于軟件功能的調測,并且便于模塊的單元測試、系統聯調等。
½ 7-3:編寫防錯程序,然后在處理錯誤之后可用斷言宣布發生錯誤。
示例:假如某模塊收到通信鏈路上的消息,則應對消息的合法性進行檢查,若消息類別不是通信協議中規定的,則應進行出錯處理,之后可用斷言報告,如下例。
#ifdef _EXAM_ASSERT_TEST_ // 若使用斷言測試
/* Notice: this function does not call 'abort' to exit program */
void assert_report( char * file_name, unsigned int line_no )
{
printf( "\n[EXAM]Error Report: %s, line %u\n",
file_name, line_no );
}
#define ASSERT_REPORT( condition )
if ( condition ) // 若條件成立,則無動作
NULL;
else // 否則報告
assert_report ( __FILE__, __LINE__ )
#else // 若不使用斷言測試
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
int msg_handle( unsigned char msg_name, unsigned char * msg )
{
switch( msg_name )
{
case MSG_ONE:
... // 消息MSG_ONE處理
return MSG_HANDLE_SUCCESS;
... // 其它合法消息處理
default:
... // 消息出錯處理
ASSERT_REPORT( FALSE ); // “合法”消息不成立,報告
return MSG_HANDLE_ERROR;
}
}
8 程序效率
¹ 8-1:編程時要經常注意代碼的效率。
說明:代碼效率分為全局效率、局部效率、時間效率及空間效率。全局效率是站在整個系統的角度上的系統效率;局部效率是站在模塊或函數角度上的效率;時間效率是程序處理輸入任務所需的時間長短;空間效率是程序所需內存空間,如機器代碼空間大小、數據空間大小、??臻g大小等。
¹ 8-2:在保證軟件系統的正確性、穩定性、可讀性及可測性的前提下,提高代碼效率。
說明:不能一味地追求代碼效率,而對軟件的正確性、穩定性、可讀性及可測性造成影響。
¹ 8-3:局部效率應為全局效率服務,不能因為提高局部效率而對全局效率造成影響。
¹ 8-4:通過對系統數據結構的劃分與組織的改進,以及對程序算法的優化來提高空間效率。
說明:這種方式是解決軟件空間效率的根本辦法。
示例:如下記錄學生學習成績的結構不合理。
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef struct STUDENT_SCORE_STRU
BYTE name[8];
BYTE age;
BYTE sex;
BYTE class;
BYTE subject;
float score;
} STUDENT_SCORE;
因為每位學生都有多科學習成績,故如上結構將占用較大空間。應如下改進(分為兩個結構),總的存貯空間將變小,操作也變得更方便。
typedef struct STUDENT_STRU
{
BYTE name[8];
BYTE age;
BYTE sex;
BYTE class;
} STUDENT;
typedef struct STUDENT_SCORE_STRU
{
WORD student_index;
BYTE subject;
float score;
} STUDENT_SCORE;
¹ 8-5:循環體內工作量最小化。
說明:應仔細考慮循環體內的語句是否可以放在循環體之外,使循環體內工作量最小,從而提高程序的時間效率。
示例:如下代碼效率不高。
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
back_sum = sum; /* backup sum */
}
語句“back_sum = sum;”完全可以放在for語句之后,如下。
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
}
back_sum = sum; /* backup sum */
½ 8-1:仔細分析有關算法,并進行優化。
½ 8-2:仔細考查、分析系統及模塊處理輸入(如事務、消息等)的方式,并加以改進。
½ 8-3:對模塊中函數的劃分及組織方式進行分析、優化,改進模塊中函數的組織結構,提高程序效率。
說明:軟件系統的效率主要與算法、處理任務方式、系統功能及函數結構有很大關系,僅在代碼上下功夫一般不能解決根本問題。
½ 8-4:編程時,要隨時留心代碼效率;優化代碼時,要考慮周全。
½ 8-5:不應花過多的時間拼命地提高調用不很頻繁的函數代碼效率。
說明:對代碼優化可提高效率,但若考慮不周很有可能引起嚴重后果。
½ 8-6:要仔細地構造或直接用匯編編寫調用頻繁或性能要求極高的函數。
說明:只有對編譯系統產生機器碼的方式以及硬件系統較為熟悉時,才可使用匯編嵌入方式。嵌入匯編可提高時間及空間效率,但也存在一定風險。
½ 8-7:在保證程序質量的前提下,通過壓縮代碼量、去掉不必要代碼以及減少不必要的局部和全局變量,來提高空間效率。
說明:這種方式對提高空間效率可起到一定作用,但往往不能解決根本問題。
½ 8-8:在多重循環中,應將最忙的循環放在最內層。
說明:減少CPU切入循環層的次數。
示例:如下代碼效率不高。
for (row = 0; row < 100; row++)
{
for (col = 0; col < 5; col++)
{
sum += a[row][col];
}
}
可以改為如下方式,以提高效率。
for (col = 0; col < 5; col++)
{
for (row = 0; row < 100; row++)
{
sum += a[row][col];
}
}
½ 8-9:盡量減少循環嵌套層次。
½ 8-10:避免循環體內含判斷語句,應將循環語句置于判斷語句的代碼塊之中。
說明:目的是減少判斷次數。循環體中的判斷語句是否可以移到循環體外,要視程序的具體情況而言,一般情況,與循環變量無關的判斷語句可以移到循環體外,而有關的則不可以。
示例:如下代碼效率稍低。
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{
if (data_type == RECT_AREA)
{
area_sum += rect_area[ind];
}
else
{
rect_length_sum += rect[ind].length;
rect_width_sum += rect[ind].width;
}
}
因為判斷語句與循環變量無關,故可如下改進,以減少判斷次數。
if (data_type == RECT_AREA)
{
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{
area_sum += rect_area[ind];
}
}
else
{
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{
rect_length_sum += rect[ind].length;
rect_width_sum += rect[ind].width;
}
}
½ 8-11:盡量用乘法或其它方法代替除法,特別是浮點運算中的除法。
說明:浮點運算除法要占用較多CPU資源。
示例:如下表達式運算可能要占較多CPU資源。
#define PAI 3.1416
radius = circle_length / (2 * PAI);
應如下把浮點除法改為浮點乘法。
#define PAI_RECIPROCAL (1 / 3.1416 ) // 編譯器編譯時,將生成具體浮點數
radius = circle_length * PAI_RECIPROCAL / 2;
½ 8-12:不要一味追求緊湊的代碼。
說明:因為緊湊的代碼并不代表高效的機器碼。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -