程序设计实践,读《程序设计实践》之一 风格

1. 名字
名字应该是非形式的、简练的、容易记忆的,如果可能的话,最好是能拼读的。
1.1 全局变量使用具有说明性的名字,局部变量用短名字
1.2 保持一致性。相关的东西应该给以相关的名字,以说明它们的关系和差异。
1.3 函数采用动作性的名字。函数名应当用动作性的动词,后面可以跟着名字。
1.4 要准确。名字不仅是个标记,它还携带着给读程序人的信息。误用的名字可能引起奇怪的程序错误。
2. 表达式和语句
2.1 用缩行显示程序的结构
2.2 使用表达式的自然形式。表达式应该写得你能大声念出来。含有否定运算的条件表达式比较难理解。
2.3 用加括号的方式排出二义性
2.4 分解复杂的表达式
eg: *x += (*xp=(2*k < (n-m) ? c[k+1] : d[k--]));
==>
if (2 * k < n - m)
*xp = c[k + 1];
else
*xp = d[k--];
*x += *xp;
2.5 要清晰。我们的目标应该是写出最清晰的代码,而不是最巧妙的代码。
2.6 担心副作用
eg: C和C++对于副作用有关的执行顺序并没有明确定义,因此下面的多次赋值语句很可能产生错误的结果:
str[i++] = str[i++] = ' ';
这样写的意图是给str中随后的两个位置赋空格值,但实际效果却要依赖于 i 的更新时刻,很可能把str里的一个位置跳过去,也可能导致只对 i 实际更新一次,应该把它分成两个语句: str[i++] = ' '; str[i++] = ' ';
3. 一致性和习惯用法
3.1 使用一致的缩排和加括号风格。实际上,特定风格远没有一致地使用它们重要。
此外,如果你工作在一个不是自己写的程序上,请注意保留程序原有的风格,程序的一致性比你本人的习惯更重要。
3.2 为了一致性,使用习惯用法
3.3 用else - if 表达多路选择。
4. 函数宏
人们使用函数宏的根本理由就是执行效率:宏可以避免函数调用的开销。在今天,函数宏的缺点远远超过它带来的好处。
4.1 避免函数宏
C++里,内联函数更削减了函数宏的用武之地。Java中根本就没有宏这种东西。
函数宏最常见的一个严重问题是: 如果一个参数在定义中出现多次,它就可能被多次求值。
eg: #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
如果isupper在下面的上下文中调用:
while (isupper(c = getchar()))
...
那么,每当遇到一个大于等于A的字符,程序就会将它丢掉,而下一个字符将被读入并去与Z做比较。重写上面的测试如下:
while ((c = getchar()) != EOF && isupper(c))
...
4.2 给宏的体和参数都加上括号
宏是通过文本替换方式实现的:定义体里的参数被调用的实际参数替换,得到的结果再作为文本去替换原来的调用段。
eg: 表达式: 1 / square(x)
如果square定义为: #define square(x) (x)*(x) 则表达式展开成: 1 / (x) * (x)
正确定义: #define square(x) ((x)*(x))
即使是在宏定义里完全加上括号,也不可能解决前面所说的多次求值的问题。所以,如果一个操作比较复杂,或者它很具一般性,值得包装起来,那么还是应该使用函数。C++提供的内联函数既避免了语法方面的麻烦,而且又可得到宏能够提供的执行效率,很适合定义那些设置或提供一个值的短小函数。
5. 神秘的数
神秘的数包括各种常数、数组的大小、字符位置、变换因子以及程序中出现的其他以文字形式写出的数值。
5.1 给神秘的数起个名字
作为一个指导原则,除了0和1之位,程序里出现的任何数大概都可以算是神秘的书,它们应该有自己的名字。
5.2 把数定义为常数,不要定义为宏
使用宏编程是一种很危险的方式,因为宏会在背地里改变程序的词法结构。
C++里任何类型都可使用const声明的常数: const int MAXROW = 24;
Java中可以使用final声明: static final int MAXROW = 24;
C语言里也有const值,但是它们不能用作数组的界。这样,enum就是C中唯一可用的选择了。
5.3 使用字符形式的常量,不要用整数。
eg: if ( c >= 65 && c <= 90) 这种写法完全依赖于特殊的字符表示方式。写成下面这样会更好些:
if ( c >= 'A' && c <= 'Z')
如果在某个字符集里的字母编码不是连续的,或者其中还夹有其他字母,那么这种描述的效果就是错误的。最好是直接使用库函数。
5.4 利用语言去计算对象的大小
不要对任何数据类型使用显示写出来的大小。
eg: sizeof(array[0])
#define NELEMS(array) (sizeof(array) / sizeof(array[0]))
6. 注释
注释是帮组程序读者的一种手段。但是,如果在注释中只说明代码本身已经讲明的事情,或者与代码矛盾,或是以精心编排的形式干扰读者,那么它们就帮了倒忙。最好的注释是简洁地点明程序的突出特征,或是提供一种概观,帮助别人理解程序。
6.1 不要大谈明显的东西
注释应该提供那些不能一下子从代码中看到的东西,或者把那些散布在许多代码里的信息收集到一起。
6.2 给函数和全局数据加注释
对于函数、全局变量、常数定义、结构和类的域等,以及任何其他加上简短说明就能够帮助理解的内容,都应该为之提供注释。
6.3 不要注释差的代码,重写它
应该注释所有不寻常的或者可能迷惑人的内容。但是如果注释的长度超过了代码本身,可能就说明这个代码应该修改了。
6.4 不要与代码矛盾
许多注释在写的时候和代码是一致的。但是后来由于修正错误,程序改变了,可是注释还保持着原来的样子,从而导致注释与代码的脱机。
注释不仅需要与代码保持一致,更应该支持它。
6.5 澄清情况,不要添乱
注释应该在困难的地方尽量帮助读者,而不是给他们设置障碍。
我们应该尽可能地把代码写得容易理解。这方面做的越好,需要写的注释就越少。
程序设计的风格:具有说明性的名字、清晰的表达式、直接了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要性。
好的风格应该成为一种习惯。
Tags: 

延伸阅读

最新评论

发表评论