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

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 澄清情况,不要添乱

    注释应该在困难的地方尽量帮助读者,而不是给他们设置障碍。

  我们应该尽可能地把代码写得容易理解。这方面做的越好,需要写的注释就越少。

程序设计的风格:具有说明性的名字、清晰的表达式、直接了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要性。

好的风格应该成为一种习惯。

你可能感兴趣的:(程序设计)