声明:关于代码阅读的研究,很多思想和文字是来自《代码阅读》这本书,再加上自己的学习和工作经验。可以说是类似读书笔记的,我把它作为了毕业论文的第8章,并结合了自己的毕设作品进行解释,毕设源代码github下载地址:https://github.com/chinaran/A-LL1-Compiler

8.3 编码规范和约定

重大的编码工作,或是在大型、有组织的体系,例如GNU和BSD中进行的项目,都会采用一套编码规范、指导准则或约定。在阅读代码时,对于特定代码元素的含义,规范和约定为我们提供了额外的指导,也因此提高了我们阅读代码的效率。尤其在企业中,如果一个项目的编码不规范,其维护的费用将是巨大的,因为项目大都是多人并行开发的,需要经常互相查看和修改别人的代码。如果你自己不按照规范写代码,不仅会带来别人的痛苦,也会带去自己的前途。

8.3.1 文件的名称和组织

大多数规范都对文件如何命名,以及使用什么扩展名进行了详细说明。我们可以考虑使用文件名和后缀名约定来优化对代码的搜索。例如编译系统中词法分析产生的二元式保存在dual.lex中,语法规则配置在gra.grammar中。

还有其他常见的文件名:README(项目概况),INSTALL(安装指示),TODO(希望将来进行扩展的清单),ChangeLog(代码更改日志),configure(平台配置脚本);

常见文件扩展名:.asm .s(汇编语言源文件),.cgi .asp .jsp .php(Web服务器上可执行的源文件),.c.cpp .java .cs .py .rb(C、C++、Java、C#、Python、Ruby源文件),.h .hpp(C、C++头文件),.class(Java的编译文件),.jar(Java库文件)。

另外,许多风格指南对于源文件中不同程序之间如何进行排序给了建议。例如,Java代码约定规定,Java类中的元素应该按照如下次序进行排列:①类变量;②实例变量③构造函数;④方法。

符合Java规范的类   图 8-4 符合Java规范的类(详见compilers.util包中的Tree.java文件)

8.3.2 格式排版

现代块结构语言编写的程序使用缩进来强调每个代码块的嵌套级别。同时风格指南通常会对用来缩进程序块的空白数量类型进行规定,例如,Java代码约束规定使用4字符宽的制表符(Tab键),而BSD风格指南则规定使用8字符宽的制表符。不幸的是,很多同学学了一两年编程也没有学会正确使用缩进,用空格或干脆顶格写,其代码体验是极其糟糕的。

由于大多数风格指南精确规定了如何对程序元素进行缩进,以便反映程序的块结构。因而可以使用代码块的缩进快速掌握其整体结构,从而帮助我们更快的阅读代码。

所有的代码规范都会对声明,以及每条具体语句的编排方式进行详细说明,其中包含空格及换行符的分布,对于花括号的放置位置有两个不同的学派。GNU、Linux,以及许多Windows程序,通常倾向于以缩进的形式将花括号放置在单独的行中,这样的好处是可以使编程元素之间更清晰。而BSD、Java程序在语句的同一行中打开花括号,并使用该语句的缩进级别来关闭花括号,这样好处是消耗的垂直空间较小,因为代码块更可能放置在单一页面中。下图显示了两种风格的代码,花括号的默认显示风格是可以在Eclipse中自由设置的。

两种不同的花括号风格   图 8-5 两种不同的花括号风格(详见compilers.scanners包中的LexicalAnalysis.java文件)

风格指南还规定了注释的编排和内容。程序的注释真的很重要,对开发者和阅读者来说都是福音。所以在开发过程中要认真写注释,尤其是在编写的功能逻辑比较复杂的时候,先写上思路和步骤再根据它编代码。下面介绍编译系统常用的注释风格,也可以在任何系统中使用。

● 以/**序列开头的Java注释会由javadoc工具处理,并自动生成源代码文档。在这样的注释中,javadoc关键字以@字符开头,如下图。

函数前的声明注释   图 8-6 函数前的声明注释(详见compilers.util包中的Tree.java文件)

● 以/* TODO或// TODO 序列开头的用来表示那些未来需要增强的地方,而且在Eclipse中可以索引到TODO列表,相当于标签的功能。

TODO 注释和 Eclipse中的 TODO Tasks 列表   图 8-7 TODO 注释和 Eclipse中的 TODO Tasks 列表

● 以/* XXX或// XXX序列开头的用来表示那些不正确但大多数时候可以工作的代码如:/* XXX is this correct*/。

● 以/* FIXME或// FIXME序列开头的用来表示那些错误的并需要修复的代码,如:/* FIXME 暂时不处理语义错误 */。

● 最后介绍下自定义的注释格式,如://step 1:…,还有下图的记录思考过程和步骤的。

记录编程思考过程和步骤的注释   图 8-8 记录编程思考过程和步骤的注释

8.3.3 命名约定

大多数编码规范中,标识符命名都是一个重要的部分。在一个项目中,命名要整体规范统一,但由于一些历史原因或随着开发人员的水平不断提高可能造成混乱,所以在项目开始规划时就要约定好,或者等待出新版本时统一调整。

命名规范主要涉及变量名、类名和函数名。不同编程语言的约定不同,有首字母大写,如TreeNode;使用下划线分割单词,如tree_node,还有只使用单词首字母,如C语言的strcmp函数(stringcompare)。但对于常量的命名规范都是一样的,使用大写字母来命名,单词与单词之间使用下划线进行分格,如ANALYSIS_STATE。编译系统是遵循Java编程约定的,包名总以一个顶级域名或项目名开始,类名和接口名义一个大写字母开始,方法和变量名以一个小写字母开始。;

还有一个稍微复杂但也是经常使用的匈牙利命名法,笔者公司用的就是这个。基本原则是:变量名 = 属性 + 类型 + 对象描述。例如一个静态的整型变量可以命名为s_nName。常见的属性部分有:全局变量 g_、常量 c_、C++类成员变量 m_、静态变量 s_;类型部分有:指针 p、函数 fn、长整型 l、布尔类型 b、浮点型 d、字符串 s、整型 n、字符 c等;描述部分有:最大 Max、最小 Min、初始化 Init、临时变量 T(或Temp)、源对象 Src、目标对象 Dest等。

当然,没有必要完全按照匈牙利法去命名,像一些循环中用到的局部变量i、j、a、b等就非常简洁。但我们还是尽量规范好,举个例子,之前在写C++程序时觉得完全没必要类成员变量前加上“m_”,真麻烦!但是工作后才发现,这是很有必要的,因为当程序比较复杂时,你可以清晰的分辨出哪个是成员变量(成员变量可以在整个类中传递状态,但不可乱用),哪个是局部变量,这对维护和扩展都是有好处的。因此,不管项目中用的是哪个命名规范,都要始终如一的使用。