一、简介
代码编写规则应该在建立一个工程项目之前、该规则应该贯穿整个项目的始终以保证代码的一致性。采用标准的代码编写惯例,可以大大简化项目的维护负担。采用一种好的风格,以达到以下目的:
可移植性
连贯
整洁
易于维护
易于理解
简洁
二、基本原则
制定标准的基本目的是加强代码的可维护性。也就是说代码必须易于阅读,易于理解,易于测试,易于移植。保持代码的简单清晰,不要在语言中使用晦涩难懂的表述,直接表明你的思想。保存一致性,尽可能使用同样的规则。避免使用复杂语句,一个语句若有太多的决策点将会使代码难于理解,尤其是对于测试。一旦修改已存在的代码,就要随时更新相关文档。
三、排版
3.1 程序块缩进
程序块要采用缩进风格编写,缩进的空格数为 4 个。在编码之前对相关 IDE 进行设置,如若没有缩进设置,需要在编写完代码后使用相关工具格式化代码
Linux :
indent
Windows :
SourceFormatX
3.2 代码群落加空行
相对独立的程序块之间、变量说明之后必须加空行。
示例:
不符合规范
void *th_func(void *arg)
{
int specific = (int)(int *)arg;
int i = 0;
for (; i <= specific; ++i)
{
printf("%lx run %d m\n", pthread_self(), i);
int time = (int)(drand48() * 10000);
usleep(time);
}
return (void *)0;
}
符合规范
void *th_func(void *arg)
{
int specific = (int)(int *)arg;
int i = 0;
for (; i <= specific; ++i)
{
printf("%lx run %d m\n", pthread_self(), i);
int time = (int)(drand48() * 10000);
usleep(time);
}
return (void *)0;
}
3.3 一行不要超过80列
较长的语句(>80 字符)要分成多行书写,长表达式要在低优先操作符处划分新行,操作符放新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。
循环、判断等语句中若有较长的表达式或语句,则要进行适当的划分,长表达式要在低优先级操作符处分新行,操作符放在新行之首。
若函数或过程中的参数较长,则要进行适当的划分。
不允许把多个短语写在一行中,即一行只写一条语句。
3.4 流程控制语句必须用大括号包含起来
if、for、do、while、case、switch、default 等语句自占一行,且 if、for、do、while 等语句的执行语句无论多少行都要加大括号 {}
3.5 操作符
在两个以上的关键字、变量、常量进行对等操作时。它们之间的操作符之前、之后或者前后都要加空格;进行非对等操作时,如果关系密切的立即操作符(如 ->),后不应加空格。
- 逗号、分号只在后面加空格
int a, b, c;
- 比较操作符,赋值操作符,算术操作法,逻辑操作符,位域操作符等双目操作符的前后都要加空格
if (a == n)
a = b + c;
a *= 2;
a = b ^ 2;
pid = p->pid;
-
单目操作符前后不加空格
*p = &q; flag = !isEmpty; ++i; j++;
- if、for、while、switch 等与后面的括号间应加空格,使if等关键字更为突出、明显
if (a >= b && c > d)
3.6 函数排版
相似的模块相似的代码顺序,相似的函数排版靠近
3.7 头文件包含次序
头文件include从抽象到具体,次序如下:
Created with Raphaël 2.1.2C标准库C++标准库OS 相关第三方库工程头文件
四、注释
注释的原则是有助于对程序的阅读理解以及提供二次开发所需文档,注释标准参考 Doxygen 代码注释规范 。遵循原则为,说明性文件、函数接口必须充分注释说明。全局变量需要说明功能及取值范围,需要自行处理资料函数需要加上使用警告信息。
不要使用注释来屏蔽代码。
关于函数和局部变量的注释,当代码已经可自注释时,不用添加多余的注释。
五、标识符命名
标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。命名中若有使用特殊约定或缩写,则要有注释说明。整个工程自始自终的命名风格保持一致,不可来回变化。
5.1 变量
对于变量命名,禁止取单个字符(如i、j、k…),建议除了要有具体含义外,还能表明其变量类型数据类型等,但i、j、k作局部循环变量是允许的。
建议命名如下:
<数据类型> [模块名]<变量作用域><变量数据类型><变量类型><变量名>
示例:
/* 全局变量 ↓ */
int comm_giv_width;
char *comm_gcp_width;
const int comm_gic_max = 10;
/* 全局变量 ↑ */
/* 局部变量 ↓ */
int liv_width;
char *lcp_width;
const int lic_max = 10;
/* 局部变量 ↑ */
/* 静态变量 ↓ */
static int lisv_width
/* 静态变量 ↓ */
5.2 函数
-
全局函数
以模块名称为前缀,单词间首字母大写,例如void CommInit()
头文件中声明必须包含extern
关键字 -
局部函数
以模块名称与下划线为前缀,单词间首字母大写。
需在源文件中做前置声明。
5.3 宏
以模块名称为前缀,单词间使用下划线隔开,单词字母为全大写
5.4 全局常量
以模块名称为前缀,单词间使用下划线隔开,单词字母为全大写
除了编译开关/头文件等特殊应用,应避免使用下划线开始和结尾的定义。
六、文件名
大模块之间以文件夹隔开。
以功能命令,源文件与头文件一一对应。
七、可读性
7.1 算法优先级
注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。防止阅读程序时产生误解,防止因默认优先级与设计思想不符而导致程序错误。
7.2 使用有意义的标识代替不易理解的数字
避免使用不易理解的数字,用有意义的标识来替代。
7.3 少用技巧性很高的语句
不要使用难懂的技巧性很高的语句,除非很有必要时。高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。
错误示例
* stat_poi ++ += 1;
* ++ stat_poi += 1;
正确示例
*stat_poi += i;
stat_poi++;
++stat_poi;
*stat_poi +=1;
八、编程建议
8.1 变量与结构
- 去掉没有必要的公共变量
- 仔细定义并明确公共变量的含义、作用、取值范围及公共变量
- 明确公共变量与操作此公共变量的函数数或过程的关系,如访问、修改及创建等。
- 当向公共变量传递数据时,要十分小心,防止赋予不合理的值或越界等现象发生。
- 防止局部变量与公共变量同名
- 严禁使用未经初始化的变量作为右值
- 构造仅有一个模块或函数可以修改、创建,而其余有关模块或函数只访问的公共变量,防止多个不同模块或函数都可以修改、创建同一公共变量。
- 使用严格形式定义的、可移植的数据类型,尽量不要使用与具体硬件或软件环境关系密切的变量。
- 结构的功能要单一,是针对一种事务的抽象。
- 不要设计面面俱到、非常灵活的数据结构。
- 不同结构间的关系不要过于复杂。
- 结构中元素的个数应适中。若结构中元素个数过多可靠率依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数
- 仔细设计结构中元素的布局与排列顺序,是结构容易理解、节省占用空间,并减少引起误用现象。
- 结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地。
- 留心具体语言及编译器处理不同数据类型的原则及有关细节
- 编程时,要注意数据类型的强制转化。
- 对编译系统默认的数据类型转化,也要有充分的认识。
- 尽量减少没有必要的数据类型默认转换与强制转换。
- 合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转化。
- 对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。注意命名方式在同一项目中的统一性。
- 当声明用于分布式环境或不同 CPU 间通信环境的数据结构时,必须考虑机器的字节顺序、使用的位域及字节对齐等问题。
8.2 函数与过程
- 对所调用函数的错误返回码要仔细、全面地处理。
- 明确函数功能,精确(而不是近似)地实现函数设计。
- 编写可重入函数时,应使用局部变量。
- 编写可重入函数时,若使用全局变量,则应通过关中断、信号量等手段对其加以保护。
- 在同一项目组应明确规定对接口函数参数的合法性检测应由函数调用者负责。
- 防止将函数的参数作为工作变量。
- 函数的规模尽量限制在150行以内。不包括注释和空行。
- 一个函数仅完成一个功能。
- 为简单功能编写函数。
- 不要设计多用途而面面俱到的函数。
- 函数的功能应该是可以预测的,保证输入数据相同就应产生同样的输出。
- 尽量不要编写依赖其它函数内部实现的函数。(
setimp 与 longjmp
) - 避免设计多参数函数,不使用的参数从接口中去掉,函数参数不要超过4个。
- 非调度函数应减少或防止控制参数,尽量只使用数据参数。
- 检查函数非参数输入的有效性,如数据文件、公共变量等。
- 函数名应准确描述函数的功能。使用动宾词组为执行某操作的函数命名。如果是OOP方法,可以只有动词(名词是对象本身)。避免使用无意义或含义不清的动词为函数命名。
- 函数的返回值要清楚、明了,让使用者不容易忽视错误情况。
- 除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。
- 在调用函数填写参数时,尽量减少没有必要的默认数据类型转换或强制数据类型转换。
- 避免函数中不必要语句,防止程序中的垃圾代码
- 防止喊出或过程内出现随机内聚。不要将没有关联或关联很弱的语句放到同一个函数或过程中。
- 如果多段代码执行同一功能,那么考虑将此段代码构造成一个新的函数。
- 功能不明确较小的函数,特别是仅有一个上级函数调用它时,应考虑把它合并到上级函数中,而不必单独存在。
- 设计高扇入、合理扇出(小于7)的函数。
- 减少函数本身或函数间的递归调用。
- 仔细分析模块的功能及性能需求,并进一步细分,同时若有必要画出有关数据流图,据此来进行模块的函数划分与组织。
- 改进模块中函数的结构,降低函数间的耦合度,并提高函数的独立性以及代码可读性、效率和可维护性。优化函数结构时,要遵守一下原则:
- 不能影响函数功能的实现。
- 仔细考查模块或函数出错处理及模块的性能要求并进行完善。
- 通过分解或合并函数来改进软件结构。
- 考查函数的规模,过大的要进行分解。
- 降低函数间接口的复杂度。
- 不同层次的函数调用要有较合理的扇入、扇出。
- 函数功能应可预测。
- 提高函数内聚。
- 在多任务操作系统的环境下编程,要注意函数可重入性的构造。
- 避免使用 BOOL 参数。
- 对于提供了返回值的函数,在引用时最好使用其返回值。
- 当一个过程(函数)中对较长变量(一般是结构体的成员)有较多引用时,可以用一个意义相当的宏代替。
8.3 可测性
- 在同一项目组或产品组内,要有一套同一的为集成测试与系统联调准备的调测开关及相应打印函数,并且要有详细的说明。
- 在同一项目组或产品组内,调测打印出的信息串的格式要有同一的形式。信息串至少要有所在模块名及行号。
- 编程的同时要为单元测试选择恰当的测试点,并仔细构造测试代码、测试用例,同时给出明确的注释说明。测试代码部分应作为(模块中的)一个子模块,以方便测试代码在模块中的安装与拆卸(通过调测开关)。
- 在进行集成测试/系统联调之前,要构造好测试环境、测试项目以及测试用例,同时仔细分析并优化测试用例,以提高测试效率。
- 使用断言来发现软件问题,提高代码可测性。
- 用断言来检查程序正常运行时不应发生但在调测时有可能发生的非法情况。
- 不能用断言来检查最终产品肯定会出现且必须处理的错误情况。
- 对较为复杂的断言加上明确的注释。
- 用断言确认函数的参数。
- 用断言保证没有定义的特性或功能不被使用。
- 用断言对程序开发环境的假设进行检查。
- 正式软件产品中应把断言及其它调测代码去掉。
- 在软件系统中设置与取消有关测试手段,不能对软件实现的功能等产生影响。
- 用调测开关来切换软件的DEBUG版和正式版,而不要同时存在正式版本和DEBUG版本的不同源文件,减少维护的难度。
- 在编写代码之前,应预先设计好程序调试与测试的方法和手段,并设计好各种调测开关及相应测试代码。
- 调测开关应分为不同级别和类型。
- 编写放错程序,然后在处理错误之后可用断言宣布发生错误。
8.4 程序效率
- 在保证软件系统的正确性、稳定性、可读性以及可测性的前提下,提高代码效率。
- 局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响。
- 通过对系统数据结构的划分与组织的改进,以及对程序算法的优化来提高空间效率。
- 循环体内工作量最小化。
- 仔细分析有关算法,并进行优化。
- 仔细考查、分析系统及模块处理输入(如事务、消息等)的方式,并加以改进。
- 对模块中函数的划分及组织方式进行分析、优化,改进模块中函数的组织结构,提高程序效率。
- 编程时,要随时留心代码效率;优化代码时,要考虑周全。
- 不应花过多的时间拼命地提高调用不很频繁的函数代码效率。
- 要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数。
- 在保证程序质量的前提下,通过压缩代码量、去掉不必要代码以及减少不必要的局部和全局变量,来提高空间效率。
- 在多重循环中,应将最忙的循环放在最内层。
- 尽量减少循环嵌套层次。
- 避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中。
- 尽量用乘法或其它方法代替除法,特别是浮点运算中的除法。
- 不要一味追求紧凑的代码。
8.5 质量保证
- 软件设计过程中构筑软件质量。
-
代码质量保证优先原则
- 正确性,指程序要实现设计要求的功能。
- 稳定性、安全性,指程序稳定、可靠、安全。
- 可测试性,指程序要具有良好的可测试性。
- 规范/可读性,指程序书写风格、命名规则等要符合规范。
- 全局效率,指软件系统的整体效率。
- 局部效率,指某个模块/子模块/函数的本身效率。
- 个人表达方式/个人方便性,指个人编程习惯。
-
只引用属于自己的存贮空间。
- 防止引用已经释放的内存空间。
- 过程/函数中分配的内存,在过程/函数退出之前要释放。
- 过程/函数中申请的文件句柄,在过程/函数退出之前要关闭。
- 防止内存操作越界。
- 认真处理程序所能遇到的各种出错情况。
- 系统运行之初,要初始化有关变量及运行环境,防止未经初始化的变量被引用。
- 系统运行之初,要对加载到系统中的数据进行一致性检查。
- 严禁随意更改其它模块或系统的有关设置和配置。
- 不能随意改变与其它模块的接口。
- 充分了解系统的接口之后,再使用系统提供的功能。
- 编程时,要防止差1错误。
- 要时刻注意易混淆的操作符。当写完程序后,应仔细检测这些操作符。
- 有可能的话,if语句尽量加上else分支,对没有else分钟的语句要小心对待;swithch语句必须有default分支。
- Unix下,多线程中的子线程退出必须采用主动退出方式。
- 不要滥用goto语句。
- 不使用与硬件或操作系统关系很大的语句,而使用建议的标准语句,以提高软件的可移植性和可重用性。
- 除非为了满足特殊需求,避免使用嵌入式汇编。
- 精心地构造、划分子模块,并按“接口”部分及“内核”部分合理地组织子模块,以提高“内核”部分的可移植性和可重用性。
- 精心构造算法,并对其性能、效率进行测试。
- 对较关键的算法最好使用其它算法来确认。
- 时刻注意表达式是否会上溢、下溢。
- 使用变量时要注意其边界值的情况。
- 留心程序机器码大小(如指令空间大小、数据空间大小、堆栈空间大小等)是否超出系统有关限制。
- 为用户提供良好的接口界面,是用户能较充分地了解系统内部运行状态及有关系统出错情况。
- 系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救。
- 对一些具有危险性的操作代码(如写硬盘、删数据等)要仔细考虑,防止对数据、硬件等的安全构成危害,以提高系统的安全性。
- 使用第三方提供的软件集开发工具包或控件时,要注意以下几点:
- 充分了解应用接口、使用环境及使用时注意事项。
- 不能过分相信其正确性。
- 除非必要,不要使用不熟悉的第三方工具包与空间。
- 资源文件需要支持多语言版本,如果资源是对语言敏感的,应让该资源与源代码文件脱离。
8.6 代码编辑、编译、审查
- 开编译器的所有告警开关对程序进行编译。
- 在产品软件中,要统一编译开关选项。
- 通过代码走读及审查方式对代码进行检查。
- 测试部测试产品之前,应对代码进行抽查及评审。
- 编写代码时,完成功能模块及自测后及时提交版本库。
- 同项目组内,最好使用相同的编辑器,并使用相同的设置选项。
- 要小心地使用编辑器提供的块拷贝功能编程。
- 合理地设计软件系统目录,方便开发人员使用。
- 某些语句经编译后产生警告,但如果你确认它是正确的,那么应通过某种手段去掉告警信息。
- 使用代码检查工具对源程序检查。
- 使用软件工具进行代码审查。
8.7 代码测试、维护
- 元测试要求至少达到语句覆盖。
- 单元测试开始要跟踪每一条语句,并观察数据流及变量的变化。
- 清理、整理或优化后的代码要经过审查及测试。
- 代码版本升级要经过严格测试。
- 使用工具软件对代码版本进行维护。
- 正式版本上软件的任何修改都应有详细的文档记录。
- 发现错误立即修改,并且要记录下来。
- 关键的代码在汇编级跟踪。
- 仔细设计并分析测试用例,是测试用例覆盖尽可能多的情况,以提高测试用例的效率。
- 尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试。
- 仔细测试代码处理数据、变量的边界情况。
- 保留测试信息,以便分析、总结经验及进行更充分的测试。
- 不应通过“试”来解决问题,应寻找问题的根本原因。
- 对自动小时的错误进行分析,搞清楚错误是如何消失的。
- 修改错误不仅要治表,更要治本。
- 测试时应设法是很少发生的事件经常发生。
- 明确模块或函数处理哪些事件,并使它们经常发生。
- 坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发现问题。
- 去除代码运行的随机性(如去掉无用的数据、代码及尽可能防止并注意函数中的”内部寄存器”等),让函数运行的结果可预测,并使出现的错误可再现。
8.8 宏
- 用宏定义表达式时,要使用完备的括号。
- 将宏定义的多条表达式放在大括号中。
- 使用宏时,不允许参数发生变化。
--------------------- 本文来自 linux_zhu 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u011641885/article/details/79585242?utm_source=copy