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.
程序設(shè)計(jì)實(shí)踐 - 第1章
1. 風(fēng)格
1.1 名字
一個(gè)名字應(yīng)該是非形式的、簡(jiǎn)練的、容易記憶的,如果可能的話(huà),最好是能夠拼讀的。
* 全局變量使用具有說(shuō)明性的名字,局部變量用短名字。
全局變量的名字應(yīng)該足夠長(zhǎng),具有足夠的說(shuō)明性。給每個(gè)全局變量聲明附一個(gè)簡(jiǎn)短注釋也非常有幫助。
對(duì)于長(zhǎng)的程序,選擇那些好的、具有說(shuō)明性的、系統(tǒng)化的名字就更加重要。
* 保持一致性。
* 函數(shù)采用動(dòng)作性名字。
* 要準(zhǔn)確。名字與其實(shí)現(xiàn)保持準(zhǔn)確的對(duì)應(yīng)。
1.2 表達(dá)式和語(yǔ)句
* 用縮行顯示程序的結(jié)構(gòu)。
采用一種一致的縮行風(fēng)格,是使程序呈現(xiàn)出結(jié)構(gòu)清晰的最省力的方法。
* 使用表達(dá)式的自然形式。
表達(dá)式應(yīng)該寫(xiě)得你能大聲念出來(lái)。
* 用加括號(hào)的方式排除二義性。
* 分解復(fù)雜的表達(dá)式。
* 要清晰。
目標(biāo)應(yīng)該是寫(xiě)出最清晰的代碼,而不是最巧妙的代碼。
* 小心副作用。
像++這一類(lèi)運(yùn)算符具有副作用,它們除了返回一個(gè)值外,還將隱含地改變變量的值。
1.3 一致性和習(xí)慣用法
* 使用一致的縮排和加括號(hào)風(fēng)格。
如果你工作在一個(gè)不是自己寫(xiě)的程序上,請(qǐng)注意保留程序原有的風(fēng)格。
* 為了一致性,使用習(xí)慣用法。
在學(xué)習(xí)一個(gè)語(yǔ)言的過(guò)程中,一個(gè)中心問(wèn)題就是逐漸熟悉它的習(xí)慣用法。
-----------------------------------------------
常見(jiàn)習(xí)慣用法之一是循環(huán)的形式。
例如:
for(i = 0; i < n; i++)
array[i] = 1.0;
C++或Java里常見(jiàn)的另一種形式是把循環(huán)變量的聲明也包括在內(nèi):
for(int i = 0; i < n; i++)
array[i] = 1.0;
-----------------------------------------------
下面是C語(yǔ)言?huà)呙枰粋€(gè)鏈表的標(biāo)準(zhǔn)循環(huán):
for(p = list; p != NULL; p = p->next)
...
-----------------------------------------------
無(wú)窮循環(huán):
for( ;; )
...
或
while(1)
...
-----------------------------------------------
常見(jiàn)的另一個(gè)慣用法是把一個(gè)賦值放進(jìn)循環(huán)條件里:
while( (c = getchar()) != EOF)
putchar(c);
-----------------------------------------------
一個(gè)不好的例子:
char *p, buf[256];
gets(buf);
p = malloc(strlen(buf));
strcpy(p, buf);
問(wèn)題1.
絕不要使用函數(shù)gets,因?yàn)槟銢](méi)辦法限制它由輸入那兒讀入內(nèi)容的數(shù)量。這常常會(huì)導(dǎo)致一個(gè)安全性問(wèn)題。
問(wèn)題2.
還有另一個(gè)問(wèn)題:strlen求出的值沒(méi)有計(jì)入串結(jié)尾的'\0'字符,而strcpy卻將復(fù)制它。
習(xí)慣寫(xiě)法是:
p = malloc(strlen(buf)+1);
strcpy(p, buf);
或在C++里:
p = new char[strlen(buf)+1];
strcpy(p , buf);
如果這里沒(méi)有+1,就要當(dāng)心。
strdup可以使避免上述錯(cuò)誤變得更簡(jiǎn)單。可惜strdup不是ANSI C標(biāo)準(zhǔn)中的內(nèi)容。
問(wèn)題3.
上面兩個(gè)版本都沒(méi)有檢查malloc的返回值。
在實(shí)際程序中,對(duì)于malloc、realloc、strdup及任何牽涉到存儲(chǔ)分配的函數(shù),它們的返回值都必須做檢查。
-----------------------------------------------
* 用else-if表達(dá)多路選擇。
多路選擇的習(xí)慣表示法形式如下:
if (condition1)
statement1
else if (condition2)
statement2
...
else if (conditionn)
statementn
else
default-statement
1.4 函數(shù)宏
* 避免使用函數(shù)宏。
函數(shù)宏最常見(jiàn)的一個(gè)嚴(yán)重問(wèn)題是:如果一個(gè)參數(shù)在定義中出現(xiàn)多次,它就可能被多次求值。
如果調(diào)用時(shí)的實(shí)際參數(shù)帶有副作用,結(jié)果就可能產(chǎn)生一個(gè)難以捉摸的錯(cuò)誤。
例子:
某<ctype.h>中:
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
如果這樣調(diào)用
while (isupper(c = getchar())),
兩次輸入的字符c被分別與'A'和'Z'比較了。
C語(yǔ)言標(biāo)準(zhǔn)允許將isupper及類(lèi)似函數(shù)定義為宏,但要求保證它們的參數(shù)只求值一次。
如果希望更安全些,那么就一定不要嵌套地使用像getchar這種帶有副作用的函數(shù)。
改寫(xiě)如下:
while ((c = getchar()) != EOF && isupper(c))
有時(shí)多次求值帶來(lái)的是執(zhí)行效率問(wèn)題,而不是真正的錯(cuò)誤。
* 給宏的體和參數(shù)都加上括號(hào)。
如果一個(gè)操作比較復(fù)雜,或者它很具一般性,值得包裝起來(lái),那么還是應(yīng)該使用函數(shù)。
C++提供的inline函數(shù)既避免了語(yǔ)法方面的麻煩,而且又可得到宏能夠提供的執(zhí)行效率,很適合來(lái)定義那些設(shè)置或者提取一個(gè)值的短信函數(shù)。
1.5 神秘的數(shù)
(翻譯的有點(diǎn)...)
神秘的數(shù)包括各種常數(shù)、數(shù)組的大小、字符位置、變換因子以及程序中出現(xiàn)的其他以文字形式寫(xiě)出的數(shù)值。
* 給神秘的數(shù)起個(gè)名字。
* 把數(shù)定義為常數(shù),不要定義為宏。
C語(yǔ)言預(yù)處理程序是一個(gè)強(qiáng)有力的工具,但是它又有些魯莽。
使用宏進(jìn)行編程是一種很危險(xiǎn)的方式,因?yàn)楹陼?huì)在背地里改變程序的詞法結(jié)構(gòu)。我們應(yīng)該讓語(yǔ)法去做正確的工作。
(譯者注:預(yù)處理命令不是C語(yǔ)言本身的組成部分,而是一組輔助成分。這里說(shuō)"讓語(yǔ)言...",也就是說(shuō)不要用預(yù)處理命令做。)
* 使用字符形式的常量,不要用整數(shù)。
例子:
if (c >= 65 && c <= 90)
...
這種寫(xiě)法完全依賴(lài)于特殊的字符表示方式。
這樣寫(xiě)更好些:
if (c >= 'A' && c <= 'Z')
...
但是,如果在某個(gè)編碼字符集里的字母編碼不是連續(xù)的,或夾有其他字母,那么這種描述就是錯(cuò)的。
最好是直接使用庫(kù)函數(shù)。
if(isupper(c))
...
-----------------------------------------------
程序里許多上下文中經(jīng)常出現(xiàn)的0。如果我們把每個(gè)0的類(lèi)型寫(xiě)得更明確更清楚,對(duì)讀程序的人理解其作用是很有幫助的。
例如,用 (void*)0 或 NULL 表示C里的空指針值,用'\0'而不是0表示字符串結(jié)尾的空字節(jié)。
然而在C++里人們都已經(jīng)接受了用0(而不是NULL)表示空指針。Java里則定義了關(guān)鍵字null。
* 利用語(yǔ)言去計(jì)算對(duì)象的大小。
不要對(duì)任何數(shù)據(jù)類(lèi)型使用顯式寫(xiě)出來(lái)的大小。
例:
char buf[1024];
fgets(buf, sizeof(buf), stdin);
對(duì)于那些可以看清楚的數(shù)組(不是指針),下面的宏定義能計(jì)算出數(shù)組的元素個(gè)數(shù):
#define NELEMS(array) (sizeof(array) / sizeof(array[0]))
1.6 注釋
* 不要大談明顯的東西。
注釋?xiě)?yīng)該提供那些不能一下子從代碼中看到的東西,或者把那些散布在許多代碼里的信息收集到一起。
* 給函數(shù)和全局?jǐn)?shù)據(jù)加注釋。
* 不要注釋差的代碼,重寫(xiě)它。
* 不要與代碼矛盾。
當(dāng)你改變代碼時(shí),一定要注意保證其中的注釋是準(zhǔn)確的。
* 澄清情況,不要添亂。
注釋?xiě)?yīng)該在困難的地方盡量幫助讀者,而不是給他們?cè)O(shè)置障礙。
注釋很重要,但是也不必盲目注釋。
注釋是一種工具,它的作用就是幫助讀者理解程序中的某些部分,而這些部分的意義不容易通過(guò)代碼本身直接看到。
1.7 為何對(duì)此費(fèi)心
注重程序設(shè)計(jì)的風(fēng)格:具有說(shuō)明性的名字、清晰的表達(dá)式、直截了當(dāng)?shù)目刂屏鳌⒖勺x的代碼和注釋?zhuān)约霸谧非筮@些內(nèi)容時(shí)一致地使用某些規(guī)則和慣用法的重要性。
書(shū)寫(xiě)良好的代碼更容易閱讀和理解,幾乎可以保證其中的錯(cuò)誤更少。
好風(fēng)格應(yīng)該成為一種習(xí)慣。
一旦這種習(xí)慣變成自動(dòng)的東西,你的潛意識(shí)就會(huì)幫助你照料許多細(xì)節(jié)問(wèn)題,甚至你在工作壓力下寫(xiě)出的代碼也會(huì)更好。