摘要:本文詳細闡述了幾種提高C代碼質量的方法。同時說明了養成良好的編程風格為設計一個高質量程序打下理論基礎。
關鍵詞:C代碼;編碼版式;編譯預處理
中圖分類號:TP316文獻標識碼:A文章編號:1009-3044(2008)11-20268-02
隨著計算機科學和通信技術的發展,計算機應用規模得到了相應的擴大,計算機軟件開發語言、工具和環境也不斷提高。由于C語言本身具有高級語言和低級語言的雙重特性、可移植性強,適合開發各種類型的軟件系統。隨著C語言的不斷發展,其編寫的軟件產品對代碼質量的要求也越來越高。筆者根據自身的體會,為了提高C語言的編寫質量,建議養成良好的編程風格,合理處理數據和函數,合理解決時間效率和空間效率的矛盾,靈活應用編譯預處理技巧等。
1 代碼的版式
程序版式雖然不會影響程序的功能,但會影響可讀性。良好的程序版式給人一種清晰,美觀的感覺,好的版式能使閱讀代碼的人對其中的函數體、變量的意義和控制語句一目了然。檢查時便于發現錯誤,節省調試時間,寫出高質量的C程序編碼。
建議一行代碼只做一件事情,如只定義一個變量,或只寫一條語句。這樣的代碼容易閱讀,并且方便于寫注釋。if、for、while等語句自占一行,執行語句不得緊跟其后。不論執行語句有多少都要加{}。這樣可以防止書寫失誤。
關鍵字之后要留空格。比如const、virtual、inline、case 等關鍵字之后至少要留一個空格,否則無法辨析關鍵字。函數名之后不要留空格,緊跟左括號‘(’,以與關鍵字區別。賦值操作符、比較操作符、算術操作符、邏輯操作符、位域操作符等二元操作符的前后應當加空格。一元操作符前后不加空格。
如果一個代碼塊在程序里的不同地方被反復使用,建議將其定義成函數。相應地,把控制轉交給函數代碼以及把控制返回給調用函數的地方,這種機制會帶來額外的時間開銷,但能提高代碼的可讀性與可維護性,這點應根據實際需要權衡。
C語言的注釋符為“/*…*/”。C++語言中,程序塊的注釋常采用“/*…*/”,行注釋一般采用“//…”。注釋是對代碼的“提示”,不可太多,花樣要少。如果代碼本來就是清楚的,則不必加注釋。邊寫代碼邊注釋,修改代碼同時修改相應的注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪除,注釋應當準確、易懂,防止注釋有二義性。當代碼比較長,特別是有多重嵌套時,應當在一些段落的結束處加注釋,便于閱讀。
2 數據代碼表示和處理
C中提供了移位運算,比如:X<<4表示將整型數據X在計算機中相對的二進制數值向左移動4位,數值末端補“0”,此操做相當于X*24,同理,X>>4將X的二進制數值向右移動4位,數值高端根據編譯器和計算機的不同組合而決定補“0”,此操做相當于X除以24,根據這種位級特性,可以優化代碼中出現的整數乘法/除法語句。計算機中,整數乘法指令的執行相當慢,需要12個或更多的時鐘周期,而除法指令則需要30個或更多的時鐘周期,但加減法、位級以及移位運算,只需要1個時鐘周期。因此,用表達式(N*(N+1))+1來計算1到N這N個整數的和以提高代碼的執行效率。盡量用乘法或其它方法代替除法,特別是浮點運算中的除法,要占用較多CPU資源,但是我們可以通過編程技巧化除為乘,提高編程效率。如下例:
例1:如下表達式運算可能要占較多CPU資源。
#define PAI 3.1416
radius = circle_length / (2 * PAI);
應如下把浮點除法改為浮點乘法。
#define PAI_RECIPROCAL (1 / 3.1416 ) // 編譯器編譯時,將生成具體浮點數
radius = circle_length * PAI_RECIPROCAL / 2;
3 合理安排程序算法的時間和空間效率
在很多C語言代碼中,最大的矛盾就是時間效率和空間效率的矛盾。我們不能一味地以空間效率換取時間效率,雖然會提高程序的執行效率,但是會占用系統的大量內存。因此,根據實際情況來合理安排時間效率和空間效率之間的關系。
通過對系統數據結構的劃分與組織的改進,以及對程序算法的優化來提高空間效率。這種方式是解決軟件空間效率的根本辦法。如例2:
例2:如下記錄學生學習成績的結構不合理。
typedef unsigned charBYTE;
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;
應仔細考慮循環體內的語句是否可以放在循環體之外,使循環體內工作量最小,從而提高程序的時間效率。如例3。
例3:如下代碼效率不高。
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 */
在C語言編程過程中,常常將一些常用的功能模塊編寫成一個獨立的函數,放在函數庫中供公共選用,編程時如果能熟練的使用函數,就可以縮短整個程序的長度和減少重復編寫程序段的工作量,但是頻繁的調用函數是會劉一程序的執行效率產生影響的。由于C語言程序在執行時,傳給函數的局部變量和參數都是以某個存儲空間為臨時存放點的,每當調用函數時,函數的返回地址也被存放在存儲空間中,所以在程序中如果頻繁的調用函數,會增大系統的負擔,也會大大降低程序的執行速度。
4 靈活應用編譯預處理技術
編譯預處理是C語言一個重要且特別的功能,靈活使用編譯預處理可使源程序結構清晰,調試簡單,C程序的設計更加方便,盡量減少不必要的調試錯誤,節省調試時間;并且能使對C編譯系統的理解更加深入,便于提高程序設計效率,便于設計出高質量的C程序。編譯預處理是C語言的一個重要特點,若能清楚理解并掌握這一技術,預處理語句主要是:包含文件,宏定義,條件編譯三大類,以下就三類預處理語句靈活應用進行分別討論。
對包含文件,預處理程序要將被包含的文件之源代碼嵌于被編譯程序的相應位置, 參加正式的編譯以目標代碼生成;一般情況下,預處理程序只按編譯系統所設的包含文件缺省路徑尋找文件名;若用“”括住一個含有路徑的文件名, 則預處理程序首先搜索該路徑所指目錄,若未找到所指文件,則再到編譯系統的缺省設定目錄下搜索,常常在編譯時發生找不到包含文件的錯誤。如果我們在預處理之前,將所有源程序放在同一目錄下,在編譯時就不會發生找不到包含文件的錯誤。
對宏定義,預處理程序要對相應符號進行宏替換;宏定義有符號常量宏定義和帶參數的宏定義兩種。符號常量宏定義的目的是給在C程序中出現的常量定義一個便于閱讀理解的名字;帶參數的宏定義可用一個直觀的名稱實現一定的函數功能。二者均可提高程序的可讀性,使程序中相應的常量、參數便于修改。需要指出的是,預處理程序對符號常量的替換不作語法檢查,只是原樣替換。我們在進行宏定義語句書寫時規范格式,以免在正式編譯時出現語法錯誤。
對條件編譯,預處理程序要根據條件確定源程序中的哪些部分要參加正式編譯形成目標代碼,哪些部分不形成目標代碼。條件編譯語句可以使編譯系統對源程序中某些段進行有選擇的編譯形成目標代碼。根據所使用條件不同,條件編譯語句一般分為兩類。一類以是否定義某一符號常量作為編譯條件;另一類則以表達式的值是否為真作為編譯條件。靈活運用條件編譯語句進行程序設計,可使所寫程序節省調用時間,提高C程序的效率。
5 結論
C語言中,同一個程序編寫方式的不同,他們的運行效率也就不同。因此在編程過程中,我們應當養成良好的編程習慣,合理安排C程序的時間和空間效率,靈活利用編譯預處理技術,在保證程序可靠性的前提下,編寫出最優的、最能充分利用系統資源的高質量C程序。
參考文獻
[1] 譚浩強,C程序設計(第二版)[M].北京:清華大學出版社,1999.
[2] 譚浩強,張缺溫,唐水炎.C程序設計教程[M].北京:高等教育出版社.1998.
[3] Bruce Eckel, Thinking in C++(C++ 編程思想,劉宗田 等譯)[M],機械工業出版社,2000.
[4] Steve Maguire, Writing Clean Code(編程精粹,姜靜波 等譯)[M],電子工業出版社,1993.
注:本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文