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 为何对此费心
注重程序设计的风格:具有说明性的名字、清晰的表达式、直截了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要性。
书写良好的代码更容易阅读和理解,几乎可以保证其中的错误更少。
好风格应该成为一种习惯。
一旦这种习惯变成自动的东西,你的潜意识就会帮助你照料许多细节问题,甚至你在工作压力下写出的代码也会更好。