The Practice of Programming

Chapter 1

命名

对于全局变量使用长的名字,局部变量使用短的名字。 对于类的属性,尽量还是使用一个完整的单词。

类的名称首字母大写,包的名称小写(由反转域名开头)。

类型参数的名称有单个字母组成,T表示任意的类型, E表示集合元素的类型,K和V表示映射的键和值类型,X表示异常。

NOTE: 同时参考了《Effective Java》的第56条。

表达式

表达式的 清晰 很重要,write clean code, not clever code.

必要时使用括号来分隔表达式,一方面为了确保优先级, 另一方面起到分组的作用,能够更加清晰。

一致性和惯用法

使整个项目的风格一致,如果维护一个项目,保持和原来一致的代码风格。

对于一些比较常用的语句,使用惯用法来表示, 比如:。

  1. 遍历数组

    for (int i = 0; i < n; ++i) { // 在C语言中,要先声明i
        // 使用array[i]
    }
    
  2. 遍历链表

    for (p = list; p != NULL; p = p->next) {
        // 使用p
    }
    
  3. 使用迭代器

    for (Iterator i = c.iterator(); i.hasNext(); ) {
        doSomething(i.next());
    }
    // 这个用法比使用while更好,因为可以在循环体里声明变量
    
  4. foreach循环(Java等语言)

    for (Element e: c) {
        doSomething(e);
    }
    // 使用foreach循环更加方便,能使用foreach循环尽量使用foreach循环。
    

strdup 函数并不在标准C中,需要glibc。

函数宏

函数宏很容易出错,尽量少用,在C++中已经被内联函数代替了。

Magic Number

给魔数起个名字,使用Enum来组织常量, 在C++中,使用const而不是define。

使用 sizeof 来计算对象的大小。

注释

注释是为了让程序员更好地理解代码,所以,代码中能够体现的就不需要通过注释来表明了。 (这与API注释不一样,API注释是给不看代码的人使用的)

给全局函数和全局变量一个注释,指明函数或者变量的用途。这和API注释有点类似。 写注释时,始终要明白这个注释是为谁而写,区别API的使用者和代码的阅读者, 虽然有时这两种人可能会重叠。

就拿一个类来说吧,如果类的属性或者方法是public的,那此时的注释就是为了API的使用者而使用的, 这时的注释应该以代码的使用方法为主,不需要涉及过多的实现细节。 而如果属性是private的,那么这些注释就是给代码的阅读者用的, 这时的注释应该讲清楚的一些关键的细节,算法或功能。

但是,大部分情况下,能够给出API类型的注释已经很不容易了,更别说为了人理解代码而写的注释。 虽然写足够清楚和有帮助的注释值得提倡, 但如果时间来不及的话,我的观点是写出足够清楚的API注释(或文档), 然后在一些关键的难以理解的过程或者算法给出代码注释, 这可能是一些比较难以理解的地方或者容易出错的地方。

write code that is easy to understand, the better you do this, the fewer comments you need.

Chapter 2

传递数组指针(或者字符串指针)时,为了指定数组的大小, 一方面可以传递一个size参数,另一方面可以在数组的末尾增加一个哨兵(比如NULL或者0), 标志者数组的结束。

对于 realloc, 先使用一个临时指针来作为结果, 这样能够保证如果出错,原来的数据不会丢失。

Prefer memmove over memcpy 。同时这个函数不需要一个个元素的修改, 减少了出错的可能。不过在C++中必须一个拷贝。

Chapter 3

从第3章的性能比较看到C++的标准库还不是很成熟,那只是00年时候的情况, 不知道现在怎么样,应该有很大改进了吧。

Chapter 4:Interfaces

设计出一个好的接口是比较困难的,需要花很长时间的考虑。

使用两个index加 memmove 来完成需要拷贝一个数组才能完成的工作, 节约空间。不过现在看起来也不是很有必要了。

接口设计原则:

  1. 封装
  2. 正交化
  3. 一致性

free 之后需要把指针指向null。

报错应该给出具体的信息,与其说输入太大,比如给出具体的范围。

Chapter 5: Debugging

现在对于这个没有太多感觉,这章的方法我也会经常用到, 现在暂时还没碰到那种解决不了的bug, 实在解决不了再来这章中找灵感吧。

Chapter 9: Notation

统一的pack和unpack

这里的处理确实很巧妙,看完整个代码之后感觉很爽。 其实思想很简单,就是通过函数把功能组织起来, 尽量加大代码的复用。 至于如何针对于不同的功能实现不同的操作, 也就是说,如果把实现不同功能的操作统一到一起, 不同的情况就要不同的处理了。 在这个例子中,是使用了一种特殊的Notation, 这样不同的函数的区别仅仅在这个notation, 而不管是什么样的notation, 都可以按照同样的方式来进行处理, 很容易扩展其他的功能。 这也正是这个notation的强大和巧妙之处。

仔细想想,正则表达式不也正是这样的一种notation吗? 通过定义不同的正则表达式可以完成不同的功能。

Mini Regular Expression

作者实现了一个简化版的正则表达式匹配, 代码不到50行,而且十分清楚。 这段代码应该在心中熟记。

尽量使用能够自动生成代码的方法,而不要自己去写, 这样能够保证正确性。