《代码大全》笔记 —— 模块三:变量

变量使用中的常规问题

  • 数据的初始化很容易出错,所以请使用本章描述的初始化技术来避免由意外初始值引起的问题。
  • 最小化每个变量的作用域。把对同一变量的引用集中在一起。把它限定在子程序或类的范围内。避免使用全局数据。
  • 处理相同变量的语句,尽可能集中在一起。
  • 早期绑定往往会限制灵活性,但有助于降低复杂度。后期绑定往往会增加灵活性,但代价是增加了复杂度。
  • 每个变量只有一个用途。

数据扫盲

简化变量声明

隐藏声明对任何一种语言来说都是最危险的特性之一。(隐式不明确的都是很危险的;builder 的 default 也很危险。)

变量初始化指南

  • 在声明时初始化每个变量
  • 在靠近首次使用的地方初始化每个变量
  • 在理想情况下,应在靠近首次使用变量的地方声明和定义每个变量
  • 尽可能使用 finalconsts
  • 特别注意计数器和累加器
  • 在构造函数中初始化该类的成员数据
  • 检查是否需要重新初始化
  • 对具名常量进行一次初始化:在可执行代码中初始化变量
  • 使用编译器设置来自动初始化所有变量
  • 利用编译器的警告信息
  • 检查输入参数的合法性
  • 使用内存访问检测工具来检查错误的指针
  • 在程序开始初始化工作内存

作用域

保持较短存活时间也是我们的目标,我们应该让变量的存活时间尽可能短。

短暂的存活时间还可以降低初始化错误的可能。

在循环之前立即初始化循环中使用的变量,而不是在包含该循环的子程序的开头进行初始化。

在使用变量之前不要为其赋值。

开始时严格限制可见性,若非必要,不宜扩大变量的作用域:缩小变量作用域的方法之一就是尽可能使其保持局部化。局部化的作用范围有助于提高智力上的可控性。

持久性

持久性的主要问题出现在假设变量的持久时间比变量的实际生命更长时。

绑定时间

数据类型和控制结构之间的关系

每个变量只有一个用途

避免使用具有隐含含义的变量。

确保使用了所有已声明的变量,与之相反的是声明了变量却不使用它。

变量名称的威力

  • 好的变量名是程序可读性的关键要素之一。对特定类型的变量,如循环索引和状态变量,需要加以特殊的考量。
  • 命名应该尽可能具体。太模糊或太宽泛、可用于多种用途的名称通常都不是好的名称。
  • 命名规范应该能够区分部数据、类数据和全局数据。它们应该还可以区分类型名、具名常量、枚举类型和变量。
  • 无论正在做哪种类型的项目,都应该采用一种变量命名规范。具体采用哪种规范取决于项目的规模及人员数量。
  • 现代编程语言很少需要使用缩写。如果确实要用缩写,请在项目字典中记录缩写或使用标准化的前缀方法。
  • 代码阅读的次数远远多于编写的次数。请确保选择的名称更倾向于便于阅读而不是只考虑写代码的时候是不是方便。

选择好名称的注意事项

变量名称要足够充分而准确地描述变量所代表的实体。一个好名称有效的技巧是用文字来表达该变量代表的是什么实体。好的名称更倾向于可以表达 “什么(what)” 而不是 “如何(how)”。

对全局命名空间中的命名使用限定符。一个惯例是要求全局可见的类以子系统助记符作为前缀。(用前缀的方式进行区分,降低冲突与误使用。)

使用类型于 Total、Sum、Average、Max、Min、Record、String 或 Pointer 这样的限定符来修改某个名称,建议把这个限定符放在名称的最后。

特定数据类型的命名

根据复杂度来选择变量名,简单循环可使用简单名。

标记的名称不应该含有 flag,因为它不能说吗该标记的用途。

一旦发现自己正在 “揣摩” 一段代码(无法一眼看明白的,即不易理解),请务必考虑重新命名这些变量。

临时变量需要一个更清晰易懂的名称。

给布尔变量赋予一个隐含真或假的命名。

使用肯定的布尔变量名:避免所有的逆否的判断。

常量的命名要根据常量所代表的抽象实体来进行,而不是根据常量所指代的数字。(避免变量值变更后变量的词不达意)

命名规范的威力

非正式的命名规范

前缀的标准化

创建刻度的短名称

缩写的指导原则如下,其中有些原则是相互冲突的,不要试图同时应用所有的原则。

  • 使用标准的缩写(在字典中列出的常用缩写)。
  • 去掉所有非前置元音。
  • 删除虚词:and,or 和 the 等。
  • 使用每个单词的第一个或前几个字母。
  • 在每个单词的第一、第二或第三个字母后持续截断,选择最合适的一个。
  • 保留每个单词的第一个和最后一个字母。
  • 使用名称中的每个重要单词,最多不超过三个。
  • 删除无用的后缀 ing 和 ed 等。
  • 保留每个音节中最引人注意的发音。
  • 确保不改变变量的含义。
  • 反复使用这些技术,直到每个变量名的长度缩减到 8-20 个字符之间,或者语言对变量名限制的字符数。

缩写的说明:

  • 不要通过从每个单词中删除一个字符来得出缩写词。
  • 缩写要保持一致。
  • 创建的名称要能够读得出来。
  • 避免导致误读或发音错误的组合。
  • 使用同义词词典来解决名称冲突。
  • 在代码中使用缩写对照表记录极短命名的含义。
  • 在一份项目级的 “标准缩写” 文档中记录所有缩写。
  • 记住,相比写代码的人,名称的意义对读代码的人更为重要。

变量名称避坑指南

  • 避免使用误导性的命名或缩写
  • 避免使用含义相似的命名
  • 避免使用含义不同但命名相似的变量
  • 避免使用发音相近的命名,如 wrap 和 rap
  • 避免在命名中使用数字
  • 避免在名称中拼错单词
  • 避免使用英语中常常拼错的单词
  • 不要仅通过大小写来区分变量名
  • 避免使用多种自然语言
  • 避免使用标准类型、变量和子程序的名称
  • 不要使用与变量所代表内容完全无关的名称
  • 避免在命名中包含容易混淆的字符

基本数据类型

  • 使用特定的数据类型意味着要为每种类型记住许多单独的规则。使用本章的核对清单来确保已经考虑到常见的问题。
  • 如果语言支持这种能力,创建自定义类型可以使程序更易于修改,并更具自我描述性。
  • 使用 typedef 或类似的方式创建了一个简单类型时,须考虑是否应该创建一个新的类。

程序中惟一能出现的字面量时 0 和 1。任何其他字面量都应该用更具描述性的内容来替换。

用中间值布尔变量可以简化复杂的判断,更方便解释程序。

数组中的随机访问就像程序中随意使用的 goto 语句一样,这种访问往往是无序的,很容易出错,而且要证明其是否正确也很困难。因此要避免这种访问。

不常见的数据类型

  • 结构体可以帮助程序变得更简单,更容易理解,更易于维护。
  • 每当打算使用结构体时,都要考虑类是否更合适。
  • 指针很容易出错。请使用访问器子程序、类以及防御性编程实践来保护自己的代码。
  • 避免用全局变量,不仅仅是因为它很危险,还因为你可以用更好的方法来替代它们。
  • 如果不能避免全局变量,那么就通过访问器子程序来处理它们。访问器子程序能为你提供全局变量所能提供的一切,甚至更多。

结构体

使用结构体简化对数据块的操作:可以将相关的元素组合到一个结构体中,并对该结构体执行操作。

使用结构体简化参数列表:可以通过使用结构体变量来简化子程序的参数列表。(使用类或结构体来使出入参结构化,避免内部参数过多过杂。)(当函数参数太对,则说明函数有问题,要么结构化,要么面向对象抽象。)

指针

隔离子程序或类中的指针操作。(通过最大限度地减少访问指针位置的数量)。

同时声明和定义指针:在靠近变量声明的地方为变量赋予初始值通常是一种良好的编程实践,这在处理指针时更有价值。

在相同的作用域层级上分配和释放指针。

使用 flag、CRC 或 md5 等方式来检查损坏的内存。

在删除或释放指针后将其设置为空。

在删除变量之前检查非法指针。

使用显式指针类型而不是默认类型。

全局数据

所有变量开始都声明为局部变量,若非必要,不要设为全局变量。

不要将所有的全局数据都放在一起。在编写访问器子程序时,请花点时间思考一下每个全局变量术语哪个类,然后将该数据及其访问器子程序与该类中的其他数据和子程序打包到一起。(全局变量也有其局部范围性)

将对数据的所有访问保持在同一抽象层上。