作者:empty 出版社:empty |
本规范适用于公司内使用C语言编码的所有软件。本规范自发布之日起生效,以后新编写的和修改的代码应守本规范。
0规范制订说明0.1前言为提高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可测试、高效、可移植的代码,编程规范修订工作组分析、总结了我司的各种典型编码问题,并参考了业界编程规范近年来的成果,重新对我司1999年版编程规范进行了梳理、优化、刷新,编写了本规范.本规范将分为完整版和精简版, 完整版将包括更多的样例、规范的解释以及参考材料(uh at&why) ,而精简版将只包含规则部分(what) 以便查阅。在本规范的最后,列出了一些业界比较优秀的编程规范,作为延伸阅读参考材料.0.2代码总体原则1、清晰第一清晰性是易于维护、易于重构的程序必需具备的特征。代码首先是给人读的,好的代码应当可以像文章一样发声朗诵出来。目前软件维护期成本古整个生命周期成木的40%~90%.根据业界经验,维护期变更代码的成本,小型系统是开发期的5倍,大型系统(100万行代码以上)可以达到100倍。业界的调查指出,开发组平均大约一半的人力用于弥补过去的错误,而不是添加新的功能来帮助公司提高竞争力。“程序必须为阅读它的人而编写,只是顺便用于机器执行.”—Harold Abelson和Gerald JaySussman“编写程序应该以人为本,计算机第二。—Steve McConnell本规范通过后文中的原则《如头优秀的代码可以自我解释,不通过注释即可轻易读懂/头文件中适合放置接的声明,不适合放置实现/除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音)、规则(如防止局部变量与全局变量同名)等说明清晰的重要性。一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。2、简洁为美简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误。写的代码越多,意味着出错的地方越多,也就意味着代码的可靠性越低。因此,我们提倡大家通过编写简洁明了的代码来提升代码可靠性。废弃的代码(没有被调用的函数和全局变量)要及时清除,重复代码应该尽可能提炼成函数,本规范通过后文中的原则(如文件应当职责单一/一个函数仅完成一件功能),规则(重复代码应该尽可能提炼成函数/避免函数过长,新增函数不超过50行)等说明简洁的重要性.3、选择合适的风格,与代码原有风格保持一致产品所有人共同分享同一种风格所带来的好处,远远超出为了统一而付出的代价。在公司已有编码规范的指导下,审慎地编排代码以使代码尽可能清晰,是一项非常重要的技能。如果重构/修改其他风格的代码时,比较明智的做法是根现有代码的现有风格继续编写代码,或者使用格式转换工具进行转2011-06-02华为机密, 未经许可不得扩散Huawei Con li dential第5页, 共61页Page 5, Total 61J MANY SLDKBA 2826-20115换成公司内部风格。0.3规范实施、解释本规范制定了编写C语言程序的基本原则、规则和建议,本规范适用于公司内使用C语言编码的所有软件。本规范自发布之日起生效,对以后新编写的和修改的代码应守本规范。
背景对于C语言来说,头文件的设计体现了大部分的系统设计。不合理的头文件布局是编译时间过长的根因,不合理的头文件实际上不合理的设计,术语定义:依赖:本章节特指编译依赖。若x.h包含了y.h,则称作x依赖y.依赖关系会进行传导,如x.h包含y.h,而y.h又包含了z.h,则x通过y依赖了z。依赖将导致编译时间的上升,虽然依赖是不可避免的,也是必须的,但是不良的设计会导致整个系统的依赖关系无比复杂,使得任意一个文件的修改都要重新编译整个系统,导致编译时间巨幅上升。在一个设计良好的系统中,修改一个文件,只需要重新编译数个,甚至是一个文件.某产品曾经做过一个实验,把所有函数的实现通过工具注释掉,其编译时间只减少了不到10%,究其原因,在于A包含B,B包含C,C包含D,最终几乎每一个源文件都包含了项目组所有的头文件,从而导致绝大部分编译时间都花在解析头文件上。某产品更有一个“优秀实践”,用于将.c文件通过工具合并成一个比较大的.c文件,从而大幅度提高编译效率。其根本原因还是在于通过合并.c文件减少了头文件解析次数,但是,这样的“优秀实践”是对合理划分,c文件的一种破坏。大部分产品够改一处代码, 都得需要编译整个工程, 对于TDD之类的实践, 要求对于模块级别的编译时间控制在秒级,即使使用分布式编译也难以实现,最终仍然需要合理的划分头文件、以及头文件之间的包含关系,从根本上降低编译时间。
若包含了头文件aa.h,则就引入了新的依赖:一旦aa.h被修改,任何直接和间接包含aa.h代码都会被重新编译。如果a a.h又包含了其他头文件如ibb.h, 那么bb.h的任何改变都将导致所有包含了a a.h的代码被重新编译,在敏捷开发方式下,代码会被频繁构建,漫长的编译时间将极大的阻碍频繁构建。因此,我们倾向于减少包含头文件,尤其是在头文件中包含头文件,以控制改动代码后的编译时间,合理的头文件划分体现了系统设计的思想,但是从编程规范的角度看,仍然有一些通用的方法,用来合理规划头文件。本章节介绍的一些方法,对于合理规划头文件会有一定的帮助。原则1.1头文件中适合放置接的声明,不适合放置实现。说明:头文件是模块(Module) 或单元(Unit) 的对外接, 头文件中应放置对外部的声明, 如对外提供的函数声明、宏定义、类型定义等,内部使用的函数(相当于类的私有方法)声明不应放在头文件中,内部使用的宏、枚举、结构定义不应放入头文件中,变量定义不应放在头文件中,应放在.c文件中,变量的声明尽量不要放在头文件中,亦即尽量不要使用全局变量作为接,变量是模块或单元的内部实现细节,不应通过在头文件中声明的方式直接暴露给外部,应通过函数接的方式进行对外暴露,即使必须使用全局变量,也只应当在.c中定义全局变量,在.h中仅声明变量为全局的,延伸阅读材料:《C语言接与实现》(David R.Hanson著傅周翻张昆琪权威译机械工业出版社2004年1月) (英文版:℃Interfaces and Implementations~)原则1.2头文件应当职责单一。说明:头文件过于复杂,依赖过于复杂是导致编译时间过长的主要原因。很多现有代码中头文件过大,职责过多,再加上环依赖的问题,可能导致为了在.c中使用一个宏,而包含十几个头文件,示例:如下是某平台定文WORD类型的头文件:
这个头文件不但定义了基本数据类型WORD, 还包含了stdio.h syslib.h等等不常用的买文件, 如果工程中有10000个源文件, 而其中100个源文件使用了stdio.h的printf, 由于上述头文件的职责过于庞大,而WORD又是每一个文件必须包含的, 从而导致stdio.h/syslib.h等可能被不必要的展开了9900次, 大大增加了工程的编译时间。原则1.3头文件应向稳定的方向包含。说明:头文件的包含关系是一种赖,一般来说,应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块,就我们的产品来说,依赖的方向应该是:产品依赖于平台,平台依赖于标准库。某产品线平台的代码中已经包含了产品的头文件,导致平台无法单独编译、发布和测试,是一个非常糟糕的反例、除了不稳定的模块依赖于稳定的模块外,更好的方式是两个模块共同依赖于接,这样任何一个模块的内部实现更改都不需要重新编译另外一个模块。在这里,我们假设接本身是最稳定的。延伸阅读材料:编者推荐开发人员使用“依赖倒置”原则,即由使用者制定接,服务提供者实现接,更具体的描述可以参见《敏捷软件开发:原则、模式与实践》(Robert C.Martin著邓辉译清华大学出版社2003年9月)的第二部分敏捷设计“章节,规则1.1每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接,说明:如果一个.c文件不需要对外公布任何接, 则其就不应当存在, 除非它是程序的入, 如main函数所在的文件。现有某些产品中,习惯一个.c文件对应两个头文件,一个用于存放对外公开的接,一个用于存放内部需要用到的定义、声明等,以控制.c文件的代码行数,编者不提侣这种风格,这种风格的根源在于源文件过大,应首先考虑拆分.c文件,使之不至于太大,另外,一旦把私有定义、声明放到独立的头文件中, 就无法从技术上避免别人include之, 难以保证这些定义最后真的只是私有的。本规则反过来并不一定成立。有些特别简单的头文件,如命令ID定义头文件,不需要有对应的.c存在。示例:对于如下场景,如在一个.c中存在数调用关系: