名字 全局變數使用描述性的名字,局部變數則使用簡潔的名字。根據定義,全局變數可以出現在整個程式中的任何地方,因此他們需要一個足夠長並且詳細描述的名字讓讀者想起它們的意義。給每個全局變數聲明附加一個簡短註釋也非常有幫助: 全局函數、類和結構應該也要有描述性的名字,以表明它們在程式里扮演的角色。對於局部 ...
名字
全局變數使用描述性的名字,局部變數則使用簡潔的名字。根據定義,全局變數可以出現在整個程式中的任何地方,因此他們需要一個足夠長並且詳細描述的名字讓讀者想起它們的意義。給每個全局變數聲明附加一個簡短註釋也非常有幫助:
int npending = 0; // current length of input queue
全局函數、類和結構應該也要有描述性的名字,以表明它們在程式里扮演的角色。
對於局部變數使用簡短的名字就夠了。在函數里,n 可能就足夠了,npoints 也還可以,用 numberOfPoints 就太過分了。
按常規方式使用的局部變數可以採用極短的名字。例如用 i、j 作為迴圈變數,p、q 作為指針,s、t 表示字元串等。比較:
for (theElementIndex = 0; theElementIndex < numberOfElements; theElementIndex++) elementArray[theElementIndex] = theElementIndex; to for (i = 0; i < nelems; i++) elem[i] = i;
對返回布爾值的函數命名,應該清楚地反映其返回值情況。因此
if (checkoctal(c)) ...
沒有表明哪一個返回值是真,哪一個是假,而:
if (isoctal(c)) ...
說清楚瞭如果參數是八進位數字則該函數返回真,否則為假。
表達式和語句
使用自然的表達式。含有否定運算的條件表達式比較難以理解:
if (!(block_id < actblks) || !(block_id >= unblocks))
在兩個測試中都用到了否定運算,有點多餘。應該改變關係運算符的方向:
if ((block_id >= actblks) || !(block_id < unblocks))
現在代碼讀起來就自然多了。
用括弧解決歧義
leap_year = y % 4 == 0 && y % 100 != 0 || y % 400 == 0; leap_year = ((y%4 == 0) && (y%100 != O)) || (y%400 == 0);
去掉了一些空格:將高優先順序運算符的操作數聚合在一起,幫助讀者更快地看清表達式的結構。
要清晰。程式員有時把自己無盡的創造力用到了儘可能地寫最簡短的代碼,或者尋求巧妙方法去得到一個結果。有時這種技能被誤用了,因為我們的目標是寫出清晰的代碼,而不是巧妙的代碼。
運算符 ?: 適用於簡短的表達式,這時它可以把4行的 if-else 程式變成1行。例如這樣:
max = (a > b) ? a : b;
當心副作用。 I/O 操作有時會帶來難以發現的問題。下麵的例子希望從標準輸入讀入兩個相關聯的數:
scanf("%d %d", &yr, &profit[yr]);
你可能認為答案依賴於參數的求值順序,但是實際上傳入 scanf 的所有參數在函數被調用前就已經計算好了,所以 &profit[yr] 實際使用的是舊的 yr 。解決辦法是把語句分解為兩個:
scanf("%d", &yr); scanf("%d", &profit[yr]);
一致性和習慣用法
如果你在一個之前不是你寫的程式上開發,應該保留程式原有的風格。即使你更喜歡你自己的風格,當你需要做修改時,也不要使用。程式的一致性比你個人的習慣更重要,對於後續跟進的人來說更方便。
函數巨集
函數巨集最常見的一個嚴重問題是:如果一個參數在定義中出現多次,它就可能被多次求值。如果調用時的實際參數帶有副作用,結果就會產生一個難以捉摸的錯誤。
下麵的代碼段來自<ctype.h>,其意圖是實現對一個字元的測試:
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
如果 isupper 被這樣子中調用:
while (isupper(c = getchar()))
getchar()函數會被調用兩次,那麼,每當遇到一個大於等於 A 的字元,程式就會將它丟掉,而下一個字元將被讀入並去與 Z 做比較,從而導致難以預料的後果。
既然巨集的缺點在很大程度上蓋過了它的優點,使用過程一不小心還會引入問題,那麼我們該在何時使用巨集呢?答案如下:
//計算出數組的元素個數 #define NELEMS(array) sizeof(array)/sizeof(array[0]) double dbuf[100]; for (i = 0; i < NELEMS(dbuf); i++) ...
在這裡,數組大小隻在一個地方設置。如果數組的大小改變,其餘代碼都不必改動。對函數參數的多次求值在這裡也不會出問題,因為它不會出現任何副作用.事實上,這個計算在程式編譯時就已經做完了。這是巨集的一個恰當使用,因為它做了某種函數無法完成的工作,從數組聲明計算出它的大小。
魔術數字
給魔術數字命名。魔術數字包括各種常數、數組的大小、字元位置等等。除了 0 和 1 之外,程式里出現的任何數大概都可以算是魔術數字,它們應該有自己的名字。
把數字定義為常數,不要定義為巨集。C 程式員的傳統方式是用 #define 行來對付神秘的數值。使用巨集進行編程是一種危險的方式,因為巨集會在背地裡改變程式的詞法結構,我們應該讓語言去做正確的工作 。在 C 和 C++ 里,整數常數可以用枚舉語句定義。在 C++ 里任何類型都可使用 const 聲明的常數:
const int MACROW = 24, MAXCOL = 80;
在 Java 中可以用 final 聲明:
static final int MACROW = 24, MAXCOL = 80;
C 語言里也有 const 值,但它們不能用作數組的邊界。所以 enum 語句仍然是在 C 裡面可選用的方法。
與此類似的還有另一個問題,那就是程式里許多上下文中經常出現的 0。雖然編譯系統會把它轉換為適當類型,但是,如果我們把每個 0 的類型寫得更明確更清楚,對讀程式的人理解其作用是很有幫助的。例如,用 (v o i d \*) 0 或 NULL 表示 C 里的空指針值,用 ‘\0’ 而不是 0 表示字元串結尾的空位元組。也就是說不要寫成:
str = 0; name[i] = 0; x = 0;
應該寫成:
str = NULL; name[i] = "\n"; x = 0;
註釋
當你改變代碼的時候,一定要註意保證對應的註釋是準確的。
以上就是我在閱讀完《程式設計實踐》第一章後做的一些筆記和總結,也算是個人博客的開篇,歡迎交流。