1.2.1 壞代碼實例
具有上述這些問題的壞代碼可以說是隨處可見。程序清單1.1是一段C代碼,它實現了一個簡單的鏈表。
程序清單1.1 一個簡單的鏈表實現(不良編碼風格)
typedef struct Linklist{ ?--- 1.應避免使用typedef類型定義。2.Linklist的命名不當。3.字符{前應該有空格。 const char * elem; struct Linklist * next; }linklist; ?--- 4.字符}后應該有空格。5.linklist的命名不當。 //初始化鏈表的函數 ?--- 6.在C程序中,應避免使用C++風格的注釋。7.應避免使用中文做注釋。8.//后缺少必要的空格。 linklist * initlinklist(); ?--- 9.initlinklist的命名不當。10.函數聲明不規范。11.表示指針的字符*應該緊貼后面的變量名或函數名。 const char * titles[]={"第1章 提高代碼可讀性","第2章 用好寫好頭文件", "第3章 消除編譯警告","第4章 常量的定義和使用", "第5章 充分利用構建系統生成器"}; ?--- 12.書寫擁擠,=兩邊應有空格。13.未適當換行和縮進。 int main() { ?--- 14.main函數的原型定義不規范。15.用于定義函數體的起始字符{應另起一行。 // 使用章節標題初始化鏈表 printf("初始化鏈表為:\n"); linklist *p=initlinklist(); ?--- 16.賦值運算符=的兩邊應有空格。 display(p); ?--- 17.未事先聲明display()函數。18.display這一術語的選擇不恰當,應考慮使用dump。 return 0; } linklist * initlinklist(){ ?--- 19.函數體之間應有空行,以便于閱讀。20.函數原型的定義不規范。21.表示函數返回值類型為指針的字符*應該緊貼后面的函數名。22.字符{的前面應該有空格。 linklist * p=NULL; //創建頭指針 ?--- 23.表示變量類型為指針的*應該緊貼變量名。24.賦值運算符=的兩邊應有空格。 linklist * temp = ( linklist*)malloc(sizeof(linklist)); ?--- 25.星號*和temp之間不應該有空格。26.左括號(和類型名稱linklist之間有多余的空格。 // 先初始化首元節點 ?--- 27.使用了不規范的術語“首元節點”。 temp->elem = titles[0]; temp->next = NULL; p = temp; // 頭指針指向首元節點 for (int i=1; i<5; i++) { ?--- 28.等號=和小于號<的兩邊應該有空格。 linklist *a=(linklist*)malloc(sizeof(linklist)); a->elem=titles[i]; a->next=NULL; temp->next=a; temp=temp->next; ?--- 29.以上5行中等號=的兩邊都應該有空格。 } 30.未縮進,應和定義循環體的for語句對齊。 return p; ?--- 31.return語句之前應有空行,用于分隔不同的功能塊。32.應縮進。 }
可以看到,在區區幾十行的代碼中,我們羅列了30多個問題(當然,大多數問題是重復的)。我們可以將這些問題歸納如下。
● 排版問題:排版問題主要表現在對齊、縮進、空格和空行的不恰當使用上。比如上述代碼在該使用空格的地方沒有使用空格,有些不該使用空格的地方卻使用空格。這一方面使代碼不夠清晰和整潔,另一方面也表現出代碼的作者在編碼時非常隨意。
● 命名問題:這段代碼中存在明顯的命名不當問題。代碼中變量和函數的命名既要注意語法,也要注意命名風格。在一開始的typedef
類型定義中,Linklist
這個名稱同時存在這兩個問題。鏈表的標準英語名稱是linked list,在把這兩個單詞組合在一起構成類型名時,應該采用LinkedList
或linked_list
這樣的形式。類似地,變量名稱linklist
在風格和語法上都存在問題。再如initlinklist()
這個函數,其命名也不規范,幾個小寫單詞擠在一起既擁擠又難看。如果使用init_linked_list()
作為函數名,用下畫線將各個單詞分隔,則會明顯提高代碼的可讀性。
● 語法問題:這段代碼存在兩方面語法問題,一方面是不必要的typedef
類型定義,另一方面是錯誤的main
函數等的原型聲明。比如這段代碼中定義的鏈表結構體,并不需要使用typedef
定義為一種新的數據類型,在代碼中直接使用struct linked_list
作為類型名稱顯然要比使用新定義的類型名稱linklist
更加清晰。原因在于通常我們會將類型定義放到頭文件中,當我們在某個源代碼文件中閱讀到使用struct linked_list *
的代碼時,不需要查看頭文件便可知悉該代碼定義了一個結構體指針,而非整數、枚舉量或者結構體。在本章的后面,我們將給出合理使用typedef
的幾個建議。另外,在這段代碼中,int main()
的寫法并不規范,規范的寫法要么是int main(void)
,要么是int main(int argc, const char *args[])
。
● 注釋問題:這段代碼中的原始注釋采用了C++風格的注釋(//
打頭),另外注釋內容使用了中文。盡管強制非英語母語的程序員使用英文寫注釋有些刻板或者嚴苛,但如果我們考慮到開源大勢以及可能的國際交流,盡量用英語書寫注釋無疑是一種合理的要求。筆者不鼓勵在產品級代碼中使用中文注釋的另外一個原因是,大量的計算機軟硬件術語最初源自英文,當我們使用中文時,由于理解或者表述上的問題,就會出現各種偏差。比如在上述代碼中,“首元節點”這一術語就顯得非常怪異。我們可以輕松理解“首”,這里大概就是指第一個;但“首元”或者“首元節點”是何意?另外,在C代碼中,僅在簡短的行尾注釋中使用C++引入的注釋方法,也是應該遵循的一個原則——這看起來有點古板,卻是優秀和專業的C程序員始終需要遵循的一項傳統或者習慣。
- PHP動態網站程序設計
- Mastering ServiceStack
- Android和PHP開發最佳實踐(第2版)
- Oracle Database In-Memory(架構與實踐)
- 劍指JVM:虛擬機實踐與性能調優
- Rake Task Management Essentials
- CKA/CKAD應試教程:從Docker到Kubernetes完全攻略
- Mastering AndEngine Game Development
- Java 11 Cookbook
- C#程序設計基礎:教程、實驗、習題
- 學習正則表達式
- 從Java到Web程序設計教程
- Spring核心技術和案例實戰
- Raspberry Pi Home Automation with Arduino(Second Edition)
- RabbitMQ Essentials