The Practice of Programming - Chapter 1 Style
1.1 Names
A name should be informative, concise, memorable, and pronounceable if possible.
* Use descriptive names for globals, short names for locals.
Global variables need names long enough and descriptive enough to remind the reader of their meaning.
It's also helpful to include a brief comment with the declaration of each global.
The longer the program, the more important is the choice of good, descriptive, systematic names.
* Be consistent.
Give related things related names that show their relationship and high-light their difference.
* Use active names for functions.
* Be accurate.
1.2 Expressions and Statements
* Indent to show structure.
A consistent indentation style is the lowest-energy way to make a program's structure self-evident.
* Use the natural form of expressions.
Write expressions as you might speak them aloud.
* Parenthesize to resolve ambiguity.
* Break up complex expressions.
* Be clear.
the goal is to write clear code, not clever code.
* Be careful with side effects.
1.3 Consistency and Idioms
* Use a consistent indentation and brace style.
* Use idioms for consistency.
A central part of learning any language is developing a familiarity with its idioms.
* Use else-ifs for multi-way decisions.
1.4 Function Macros
* Avoid function macros.
* Parenthesize the macro body and arguments.
In C++, inline functions avoid the syntactic trouble while offering whatever performance advantage macros might provide.
They are appropriate for short functions that set or retrieve a single value.
1.5 Magic Numbers
Magic numbers are the constants, array sizes, character positions, conversion factors, and other literal numric values that appear in programs.
* Given names to magic numbers.
* Define numbers as constants, not macros.
The C preprocessor is a powerful but blunt tool, however, and macros are a dangerous way to program because they change the lexical structure of the program underfoot. Let the language proper do the work.
* Use character constants, not integers.
* Use the language to calculate the size of an object.
1.6 Comments
* Don't belabor the obvious.
* Comment functions and global data.
* Don't comment bad code, rewrite it.
* Don't contradict the code.
* Clarify,don't confuse.
1.7 Why bother?
The main concerns of programming style: descriptive names, clearity in expressions, straightforward control flow, readability of code and comments, and the importance of consistence use of conventions and idioms in achieving all of these.
Well-written code is easier to read and to understand, almost surely has fewer errors, and is likely to be smaller than code that has been carelessly tossed together and never polished.
The key observation is that good style should be a matter of habit.
Once they become automatic, your subconscious will take care of many of the details for you, and even the code you produce under pressure will be better.
程序設計實踐 - 第1章
1. 風格
1.1 名字
一個名字應該是非形式的、簡練的、容易記憶的,如果可能的話,最好是能夠拼讀的。
* 全局變量使用具有說明性的名字,局部變量用短名字。
全局變量的名字應該足夠長,具有足夠的說明性。給每個全局變量聲明附一個簡短注釋也非常有幫助。
對于長的程序,選擇那些好的、具有說明性的、系統化的名字就更加重要。
* 保持一致性。
* 函數采用動作性名字。
* 要準確。名字與其實現保持準確的對應。
1.2 表達式和語句
* 用縮行顯示程序的結構。
采用一種一致的縮行風格,是使程序呈現出結構清晰的最省力的方法。
* 使用表達式的自然形式。
表達式應該寫得你能大聲念出來。
* 用加括號的方式排除二義性。
* 分解復雜的表達式。
* 要清晰。
目標應該是寫出最清晰的代碼,而不是最巧妙的代碼。
* 小心副作用。
像++這一類運算符具有副作用,它們除了返回一個值外,還將隱含地改變變量的值。
1.3 一致性和習慣用法
* 使用一致的縮排和加括號風格。
如果你工作在一個不是自己寫的程序上,請注意保留程序原有的風格。
* 為了一致性,使用習慣用法。
在學習一個語言的過程中,一個中心問題就是逐漸熟悉它的習慣用法。
-----------------------------------------------
常見習慣用法之一是循環的形式。
例如:
for(i = 0; i < n; i++)
array[i] = 1.0;
C++或Java里常見的另一種形式是把循環變量的聲明也包括在內:
for(int i = 0; i < n; i++)
array[i] = 1.0;
-----------------------------------------------
下面是C語言掃描一個鏈表的標準循環:
for(p = list; p != NULL; p = p->next)
...
-----------------------------------------------
無窮循環:
for( ;; )
...
或
while(1)
...
-----------------------------------------------
常見的另一個慣用法是把一個賦值放進循環條件里:
while( (c = getchar()) != EOF)
putchar(c);
-----------------------------------------------
一個不好的例子:
char *p, buf[256];
gets(buf);
p = malloc(strlen(buf));
strcpy(p, buf);
問題1.
絕不要使用函數gets,因為你沒辦法限制它由輸入那兒讀入內容的數量。這常常會導致一個安全性問題。
問題2.
還有另一個問題:strlen求出的值沒有計入串結尾的'\0'字符,而strcpy卻將復制它。
習慣寫法是:
p = malloc(strlen(buf)+1);
strcpy(p, buf);
或在C++里:
p = new char[strlen(buf)+1];
strcpy(p , buf);
如果這里沒有+1,就要當心。
strdup可以使避免上述錯誤變得更簡單。可惜strdup不是ANSI C標準中的內容。
問題3.
上面兩個版本都沒有檢查malloc的返回值。
在實際程序中,對于malloc、realloc、strdup及任何牽涉到存儲分配的函數,它們的返回值都必須做檢查。
-----------------------------------------------
* 用else-if表達多路選擇。
多路選擇的習慣表示法形式如下:
if (condition1)
statement1
else if (condition2)
statement2
...
else if (conditionn)
statementn
else
default-statement
1.4 函數宏
* 避免使用函數宏。
函數宏最常見的一個嚴重問題是:如果一個參數在定義中出現多次,它就可能被多次求值。
如果調用時的實際參數帶有副作用,結果就可能產生一個難以捉摸的錯誤。
例子:
某<ctype.h>中:
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
如果這樣調用
while (isupper(c = getchar())),
兩次輸入的字符c被分別與'A'和'Z'比較了。
C語言標準允許將isupper及類似函數定義為宏,但要求保證它們的參數只求值一次。
如果希望更安全些,那么就一定不要嵌套地使用像getchar這種帶有副作用的函數。
改寫如下:
while ((c = getchar()) != EOF && isupper(c))
有時多次求值帶來的是執行效率問題,而不是真正的錯誤。
* 給宏的體和參數都加上括號。
如果一個操作比較復雜,或者它很具一般性,值得包裝起來,那么還是應該使用函數。
C++提供的inline函數既避免了語法方面的麻煩,而且又可得到宏能夠提供的執行效率,很適合來定義那些設置或者提取一個值的短信函數。
1.5 神秘的數
(翻譯的有點...)
神秘的數包括各種常數、數組的大小、字符位置、變換因子以及程序中出現的其他以文字形式寫出的數值。
* 給神秘的數起個名字。
* 把數定義為常數,不要定義為宏。
C語言預處理程序是一個強有力的工具,但是它又有些魯莽。
使用宏進行編程是一種很危險的方式,因為宏會在背地里改變程序的詞法結構。我們應該讓語法去做正確的工作。
(譯者注:預處理命令不是C語言本身的組成部分,而是一組輔助成分。這里說"讓語言...",也就是說不要用預處理命令做。)
* 使用字符形式的常量,不要用整數。
例子:
if (c >= 65 && c <= 90)
...
這種寫法完全依賴于特殊的字符表示方式。
這樣寫更好些:
if (c >= 'A' && c <= 'Z')
...
但是,如果在某個編碼字符集里的字母編碼不是連續的,或夾有其他字母,那么這種描述就是錯的。
最好是直接使用庫函數。
if(isupper(c))
...
-----------------------------------------------
程序里許多上下文中經常出現的0。如果我們把每個0的類型寫得更明確更清楚,對讀程序的人理解其作用是很有幫助的。
例如,用 (void*)0 或 NULL 表示C里的空指針值,用'\0'而不是0表示字符串結尾的空字節。
然而在C++里人們都已經接受了用0(而不是NULL)表示空指針。Java里則定義了關鍵字null。
* 利用語言去計算對象的大小。
不要對任何數據類型使用顯式寫出來的大小。
例:
char buf[1024];
fgets(buf, sizeof(buf), stdin);
對于那些可以看清楚的數組(不是指針),下面的宏定義能計算出數組的元素個數:
#define NELEMS(array) (sizeof(array) / sizeof(array[0]))
1.6 注釋
* 不要大談明顯的東西。
注釋應該提供那些不能一下子從代碼中看到的東西,或者把那些散布在許多代碼里的信息收集到一起。
* 給函數和全局數據加注釋。
* 不要注釋差的代碼,重寫它。
* 不要與代碼矛盾。
當你改變代碼時,一定要注意保證其中的注釋是準確的。
* 澄清情況,不要添亂。
注釋應該在困難的地方盡量幫助讀者,而不是給他們設置障礙。
注釋很重要,但是也不必盲目注釋。
注釋是一種工具,它的作用就是幫助讀者理解程序中的某些部分,而這些部分的意義不容易通過代碼本身直接看到。
1.7 為何對此費心
注重程序設計的風格:具有說明性的名字、清晰的表達式、直截了當的控制流、可讀的代碼和注釋,以及在追求這些內容時一致地使用某些規則和慣用法的重要性。
書寫良好的代碼更容易閱讀和理解,幾乎可以保證其中的錯誤更少。
好風格應該成為一種習慣。
一旦這種習慣變成自動的東西,你的潛意識就會幫助你照料許多細節問題,甚至你在工作壓力下寫出的代碼也會更好。