Featured image of post 读《代码大全(第二版)》笔记(三)

读《代码大全(第二版)》笔记(三)

继续上一周的阅读,本周读了第三部分-变量。

整个第三部分,包括第 10 章到第 13 章,一共有四章都是围绕变量展开。可见变量在程序设计当中有多重要。

第十章-使用变量的一般事项

从本章标题中也可以看出,本章主要就是在讲变量的使用。

本章一开始,作者就提到关于隐式变量声明的观点。例如:

(Page.240)隐式变量声明对于任何一种语言来说都是最具危险性的特性之一

作者给出的解决办法是:

  • 关闭隐式声明
  • 声明全部的变量
  • 遵循某种命名规则
  • 检查变量名

其实,关于这一点,现代解释型脚本语言貌似很多都支持隐式声明,例如Javascript。但是作者说的并不无道理,我用脚本语言用的还不是很多,没有太多经验来总结其中的利弊,暂且采用作者说的方法。

在作用域一节中,作者提出了两个概念,一个是

(Page.245)跨度*”,另一个是“*(Page.246)存活时间(live time)。

所谓跨度,作者引出了其定义

(Page.245)衡量又一个变量的不同引用点的靠近程度的一种方法是计算该变量的“跨度(span)。

所谓存活时间,

(Page.246)即一个变量存在期间所跨越的语句总数。

也由此可见,对于变量来说保持跨度值和存活时间尽可能的短是主要目标。

对于减小变量作用域,作者给出了一些建议。

(Page.249)在循环开始之前再去初始化该循环里使用的变量,而不是在该循环所属的子程序的开始处初始化这些变量

对于这一条建议,作者的目的是能够在循环里正确的使用外部初始化的变量。比如你把一个变量的声明和使用这个变量的循环之间隔离的很远的话,当需要修改循环的时候比较不容易看到循环中使用的这个变量。

(Page.249)直到变量即将被使用时再为其赋值

这一条建议,我觉得很容易理解,主要就是为了让代码的初始化更醒目。

(Page.250)当对变量的作用域犹豫不决的时候,你应该倾向于选择该变量所能具有的最小作用域

这条建议,其实目的也是为了尽可能的降低变量的生存时间。

(Page.252)采用越晚的绑定时间会越有利

关于绑定时间书中给出了三种情况:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Java示例:在编写代码时绑定其值的变量
titleBar.color = 0xFF; //0xFF is hex value for color blue

// Java示例:在编译时绑定其值的变量
private static final int COLOR_BLUE = 0xFF;
private static final int TITLE_BAR_COLOR = COLOR_BLUE;
...
titleBar.color = TITLE_BAR_COLOR;

// Java示例:在运行时绑定其值的变量 titleBar.color =
ReadTitleBarColor();

关于这一点就不多做解释了。示例已经说明问题了。

第十一章-变量名的力量

全章讲述的是变量命名,对于大的项目而言,好的变量名不但使程序更容易读懂和编写,而且还利于以后的 bug 调试。

对于如何命名,书中提到了一点建议,我觉得之前可能做的也不太好。

(Page.261)一个好记的名字反映的通常都是问题,而不是解决方案。一个好名字通常表达的是“什么”(what),而不是“如何”(how)。

作者说,

(Page.261)一般而言,如果一个名字反映了计算的某些方面而不是问题本身,那么它反映的就是“how”而非“what”了。

关于名字的长度,文中提到

(Page.262)当变量名的平均长度在 10 到 16 个字符的时候,调试程序所需花费的力气是最小的(1990)。

其实,我觉得, Objective C 这门语言天生就挺符合这个要求的,不用你刻意去注意,哈哈。

对于变量中使用计算限定词,文中也给出了很好的建议。

(Page.263)如果你要用类似与 Total、Sum、Average、Max、Min、Record、String、Pointer 这样的限定词来修改某个名字,那么请记住把限定词加到限定词名字的最后。

对于文中所说何时采用命名规则,我觉得,平时就养成这个习惯吧,不论怎样,这个习惯都不是一个坏习惯。

与语言无关的命名规则的指导原则,书中也给出了几种方案(Page.272)。

方案 1: 通过大写字母开头区分类型和变量,例如: Widget 与 widget; LongerWidget 与 longerWidget;

方案 2: 通过全部大写区分类型和变量,例如: WIDGET 与 widget; LONGERWIDGET 与 longerWidget;

方案 3: 通过给类型“t_”前缀区分类型和变量,例如: t_Widget 与 widget; t_LongerWidget 与 LongerWidger;

方案 4: 通过给变量“a”前缀区分类型和变量,例如: Widget 与 aWidget; LongerWidget 与 aLongWidget;

方案 5: 通过对变量采用更明确的名字区分类型和变量,例如: Widget 与  employeeWidget; LongerWidget   与 fullEmployeeWidget;

书中对每一种方案的优缺点做了描述(Page.272~Page.273):

  • 第一种方案是在大小写敏感语言如 C++ 和 Java 里常用的规则,但是有些程序员对仅依靠大小写区分名字感到不大舒服。

  • 第二种方案使类型名和变量名之间的差异更加鲜明。然而,由于历史原因,在 C++ 和 Java 里面全部字母大写只用于表示常量,同时这种方案也会与第一种方案一样面临混合语言环境的问题。

  • 第三种方案可用于所有语言,但是很多程序员从审美的角度出发并不喜欢增加前缀。

  • 第四种方案有时会用作第三种方案的备选项,但是它存在的问题是需要改变类的每个实例的名字,而不是仅仅修改类名。

  • 第五种方案要求基于每个变量的实际情况作出更多的考虑。

本章后面说了下应该避免的命名指导原则,原则有很多,我觉得都是非常常见的命名错误,在这里就不罗列了。

第十二章-基本数据类型

本章对每一种基本数据类型的使用进行了详细讲解。首先从全局上给出了一些建议(Page.292~Page.293)。

  • 避免使用“神秘数值(magic number)” 。如果需要,可以使用硬编码的 0 和 1
  • 预防除零(devide-by-zero)错误,使类型转换变得明显。
  • 避免混合类型的比较,注意编译器的警告。

本章后面的基本类型差不多都是围绕着上面这几点根据具体的类型特点展开来讲的。其中印象比较深刻的是关于浮点数的一条建议:

(Page.295)避免等量判断

很早之前掉进过这个坑。记得当时用了两个浮点数值进行等值判断,结果相等的概率非常的低,后来打印了一下数值,才发现掉到坑去里了,后来就长记性了,呵呵。

对于C语言的字符串的使用,有几个建议印象比较深刻。

(Page.299)把 C-Style 字符串的长度声明为 CONSTANT + 1

这一条建议可以避免循环数组时越界,当然这种写法在很多算法书中也很常见。

(Page.300)用 null 初始化字符串以避免无结束符的字符串

这条在平时初始化的时候也很受用。下面这条是关于分配内存并初始化的建议。

(Page.300)其次,在你动态分配字符串的时候,使用 calloc()而不是 malloc()来把它初始化为 0。calloc()会负责分配内存,并把它初始化为 0。malloc()只分配内存,并不执行初始化。……

对于定义枚举类型,文中给出了两条之前没有注意的点。

(Page.305)定义出枚举的第一项和最后一项,以便用于循环边界

这一点以前还真没注意过,以后要注意了。还有下一点,我觉得也是蛮有道理的,虽然表面上感觉有点小聪明的意思,呵呵。

(Page.305)把枚举类型的第一个元素留作非法值

本章后面谈论了一下使用typedef进行自定义类型,感觉没有太多要说的。

第十三章-不常见的数据类型

本章主要是在讲非基本数据类型。主要包括结构、指针、全局数据。其实,关于这一章里,结构没有什么好说的,很多常见的语法书中该说的基本都说到了,包括结构化数据的优点等。关于 C/C++ 语言指针部分,其实很多基本概念在一些权威的书中基本也都提到过,没什么感觉。最后的讲了全局数据,当然主要就是全局数据的弊端占用了很多篇幅,以及如何避免使用全局数据等。避免的方式,感觉还是通过重构,尽量使程序模块化,降低变量的生存时间,也避免在内存堆中分配空间,达到节省内存的目的等等。

总体来说,这部分的内容不少,但是有些知识已经在一些权威的语法书中能够找到,读起来相对来说没有前两部分难懂,可能有些问题是平时写程序的时候经常会碰到的原因吧,见的多了,神经就有点麻木了,呵呵。