目录
[1. 基本原则](#1. 基本原则)
[2. 布局](#2. 布局)
[3. 注释](#3. 注释)
[4. 命名规则](#4. 命名规则)
[5. 变量、常量与类型](#5. 变量、常量与类型)
[6. 表达式与语句](#6. 表达式与语句)
[7. 函数与过程](#7. 函数与过程)
[8. 可靠性](#8. 可靠性)
[9. 可测试性](#9. 可测试性)
前言
编码规范是成为一个优质程序员的重要一课,它是编程的样式的模板。
为了跟大佬们一样写出简洁、可维护、可靠、可测试、高效、可移植的代码,我参考网上的资料做了一个归类。
一、编程规范的作用
1.提高源程序的可读性和可维护性
2.降低错误的机会
3.提高源代码可重用性和质量
二、规范的三种形式
1.原则:编程时应该坚持的指导思想
2.规则:编程时必须遵守的约定
3.建议: 编程时必须加以考虑的约定
三、规范的内容
1. 基本原则
原则1-1
首先是为人编写程序,其次才是计算机。
说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发、测试、生产、用户使用、版本升级和后期维护等长期过程,只有易读、易维护的软件代码才具有生命力。
原则1-2
保持代码的简明清晰,避免过分的编程技巧。
简单是最美。保持代码的简单化是软件工程化的基本要求。不要过分追求技巧,否则会降低程序的可读性。
原则1-3
所有的代码尽量遵循ANSI C标准
所有的代码尽可能遵循ANSI C标准,尽可能不使用ANSI C未定义的或编译器扩展的功能。
原则1-4
编程时首先达到正确性,其次考虑效率。
编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。
原则1-5
避免或少用全局变量
过多地使用全局变量,会导致模块间的紧耦合违反模块化的要求
原则1-6
尽量遥免使用GOTO语句
原则1-7
尽可能复用、修正老的代码。
尽量选择可借用的代码,对其修改优化以达到自身要求。
2. 布局
规则2-1-1
遵循统一的布局顺序来书写头文件#ifmndef 文件名 H(全大写)
#defie
文件名 H
其它条件编译选项
#include(依次为标准库头文件、非标准库头文件)常量定义
全局宏
全局数据类型
类定义
模板 (template)(包括C++中的类模板和函数模板) 全局函数原型
#endif
规则2-1-2
遵循统一的布局顺序来书写实现文件
文件头注释
#include(依次为标准库头文件、非标准库头文件)常量定义
文件内部使用的宏
文件内部使用的数据类型
全局变量
本地变量(即静态全局变量)
局部函数原型
类的实现全局函数局部函数
规则2-1-3
使用注释块分离上面定义的节
规则2-1-4
头文件必须要避免重复包含
规则2-1-5
包含标准库头文件用尖括号 < >
包含非标准库头文件用双引号 " "
规则2-1-6
遵循统一的顺序书写类的定义及实现
类的定义(在定义文件中) 按如下顺序书写:
- 公有属性,公有函数
- 保护属性,保护函数
- 私有属性,私有函数
类的实现(在实现文件中) 按如下顺序书写:
- 构造函数,析构函数
- 公有函数
- 保护函数
- 私有函数
规则2-2-1
程序中一行的代码和注释不能超过80列
包括空格在内不超过80列
规则2-2-2
if、 else、 else if、 for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{ }
规则2-2-3
结构型的数组、多维的数组如果在定义时初始化,按照数组的矩阵结构分行书写。
建议2-3-1
在switch语句中,每一个case分支和default要用{ }括起来,{ }中的内容需要缩进。
规则2-4-1
不同逻辑程序块之间要使用空行分隔
规则2-4-2
多元运算符和它们的操作数之间至少需要一个空格。
规则2-4-3
关键字之后要留空格
if、for、while等关键字之后应留一个空格再跟左括号 '(',以突出关键字
另外 sizeof 是关键字,strlen 是函数
规则2-4-4
函数名之后不要留空格
函数名后紧跟左括号 '(',以与关键字区别
规则2-4-2
不是行结束符号时其后要留空格
规则2-4-6
注释符与注释内容之间要用一个空格进行分隔
规则2-5-1
函数声明时,类型与名称不允许分行书写
3. 注释
规则3-1
文件头部必须进行注释,包括:.h文件、.c文件、.cpp文件、.inc文件、.def文件、编译说明文件.cfg等。
注释必须列出:版权信息、文件标识、内容摘要、版本号、作者、完成日期、修改信息等
规则3-2
函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、访问和修改的表、修改信息等。
说明:注释必须列出:函数名称、功能描述、输入参数、输出参数、返回值、修改信息等
规则3-3
包含在中代码块的结束处应加注释,便于阅读。
特别是多分支、多重嵌套的条件语句或循环语句。
规则3-4
注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释) 相邻位置不可放在下面,如放于上方则需与其上面的代码用空行隔开
4. 命名规则
规则4-1
标识符要采用英文单词或其组合,便于记忆和阅读,切总使用汉语拼音来命名。
标识符应当直观且可以拼读,可望文知义,避免使人产生误解。程序中的英文单词一般不要太复杂,用词应当准确。
规则4-2
标识符的命名应当符合"min-lengt && max-information"原则
较短的单词可通过去掉"元音"形成缩写,较长的单词可取单词的头几个字母形成缩写,一些单词有大家公认的缩写,常用单词的缩写必须统一。协议中的单词的缩写与协议保持一致对于某个系统使用的专用缩写应该在某处做统一说明。
例如:
- temp 可缩写为 tmp ;
- flag 可缩写为 flg ;
- statistic 可缩写为 stat ;
- increment 可缩写为 inc ;
- message 可缩写为 msg ;
规则4-3
用正确的反义词组命名具有互斥意义的变量或相反动作的函数等
- add / remove;
- begin / end;
- create / destroy;
- insert / delete;
- first / last;
- get / release;
- increment / decrementputget;
- lock / unlock ;
- open / close ;
- min / max;
- old / new;
- start / stop;
- source / target ;
- next / previous;
- show / hide;
- send / receive ;
- source / destination;
- cut / paste;
- up / down;
规则4-4
宏、常量名都要使用大写字母,用下划线 '_' 分割单词。
预编译宏定义使用下划线 '_' 开始。
例如:
DISP_BUF_SIZE、MIN_VALUE、MAX_VALUE
规则4-5
变量名长度应小于31个字符,以保持与ANSI C标准一致。
不得取单个字符(如i、j、k等)作为变量名,但是局部循环变量除外。
规则4-6
程序中局部变量不要与全局变量重名
尽管局部变量和全局变量的作用域不同而不会发生语法错误,但容易使人误解。
规则4-7
使用一致的前缀来区分变量的作用域
变量活动范围前缀规范如下:
规则4-8
使用一致的小写类型指示符作为前缀来区分变量的类型。
规则4-9
完整的变量名应由前缀+变量名主体组成,变量名的主体应当使用"名词"或者"形容词 + 名词",且首字母必须大写。
例如:
- float g fValue; // 类型为浮点数的全局变量
- char *pcOldChar; // 类型为字符指针的局部变量char
规则4-10
- 结构名、联合名、枚举名由前缀T_开头。
- 事件名由前缀EV 开头
规则4-11
类名采用大小写结合的方法。在构成类名的单词之间不用下划线,类名在开头加上C,类的成员变量统一在前面加m_前缀
建议4-12
尽量避免名字中出现数字编号,如Value1、Value2等,除非逻辑上的确需要编号
建议4-13
标识符前最好不加项目、产品、部门的标识
这样做的目的是为了代码的可重用性
5. 变量、常量与类型
规则5-1
宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。
宏定义中,对表达式和变量使用括号,可以避免可能发生的计算错误
正例:
#define HANDLE(A,B) (( A ) / ( B ))
反例:
#define HANDLE(A,B) (A / B)
规则5-2
使用宏定义多行语句时,必须使用把这些语句括起来。
在宏定义中,对多行语句使用大括号,可以避免可能发生的错误。
建议5-3
结构中元素的个数应适中。
若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数
6. 表达式与语句
规则6-1
一条语句只完成一个功能
规则6-2
在表达式中使用括号,使表达式的运算顺序更清晰。
由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。
规则6-3
避免表达式中的附加功能,不要编写太复杂的复合表达式。
规则6-4
不可将布尔变量和逻辑表达式直接与TRUEFALSE或者1、0进行比较。TURE和FALSE的定义值是和语言环境相关的,且可能会被重定义的
规则6-5
在条件判断语句中,当整型变量与0比较时,不可模仿布尔变量的风格,应当将整型变量用"=="或"!="直接与0比较。
规则6-6
不可将浮点变量用"=="或"!="上任何数字比较
规则6-7
应当将指针变量用"=="或"!="与NULL比较
规则6-8
在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。
规则6-9
不可在for 循环体内修改循环变量防止for 循环失去控制。
建议6-10
循环嵌套次数不大于3次
7. 函数与过程
规则7-1
如果函数没有参数,则用void填充
规则7-2
如果参数是指针,且仅作输入用,则应在类型前加const。
防止该指针在函数体内被意外修改
例如:
int GetStrLen(const char *pcString):
规则7-3
不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型
- C语言中,凡不加类型说明的函数,一律自动按整型处理。如果不注明类型,容易被误解为void类型,产生不必要的麻烦
- C++语言有很严格的类型安全检查,不允许上述情况发生。由于C++程序可以调用C函数,为了避免混乱,规定任何C/ C++函数都必须有类型。
规则7-4
对于有返回值的函数,每一个分支都必须有返回值。
为了保证对被调用函数返回值的判断,有返回值的函数中的每一个退出点都需要有返回值。
规则7-5
防止将函数的参数作为工作变量
将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
建议7-6
如果返回值表示函数运行是否正常规定0为正常退出,不同非0值标识不同异常退出。避免使用TRUE或FALSE作为返回值
建议7-7
函数体的规模不能太大,尽量控制在200行代码之内
建议7-8
减少函数本身或函数间的递归调用
递归调用特别是函数间的递归调用 (如A->B->C->A),影响程序的可理解性,递归调用一般都占用较多的系统资源(如栈空间) ;递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。
对于前台软件为了系统的稳定性和可靠性,往往规定了进程的堆栈大小。如果采用了递归算法,收敛的条件又往往难以确定,很容易使得进程的堆栈溢出,破坏系统的正常运行:另外,由于无法确定递归的次数,降低了系统的稳定性和可靠性。
建议7-9
设计高扇入、合理扇出的函数
扇出是指一个函数直接调用(控制) 其它函数的数目,而扇入是指有多少上级函数调用它。
扇出过大,表明函数过分复杂,需要控制和协调过多的下级函数:而扇出过小,如总是1,表明函数的调用层次可能过多,这样不利于程序阅读和函数结构的分析,并且程序运行时会对系统资源如堆栈空间等造成压力。函数较合理的扇出(调度函数除外) 通常是3-5。扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数进步分解成多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。
扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。
8. 可靠性
规则8-1
在程序编制之前,必须了解编译系统的内存分配方式,特别是编译系统对不同类型的变量的内存分配规则,如局部变量在何处分配、静态变量在何处分配等。
规则8-2
防止内存操作越界
内存操作主要是指对数组、指针、内存地址等的操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细。
规则8-3
必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对以防止内存泄漏,释放后内存指针置为NULL。
- 对嵌入式系统,通常内存是有限的,内存的申请可能会失败,如果不检查就对该指针进行操作,可能出现异常,而且这种异常不是每次都出现比较难定位。
- 指针释放后,该指针可能还是指向原有的内存块,可能不是,变成个野指针,一般用户不会对它再操作,但用户失误情况下对它的操作可能导致程序崩溃。
规则8-4
不使用realloc()。
调用realloc对一个内存块进行扩展,导致原来的内容发生了存储位置的变化, realloc函数既要调用free,又要调用malloc。执行时究竟调用哪个函数,取决于是要缩小还是扩大相应内存块的大小
规则8-5
变量在使用前应初始化,防止未经初始化的变量被引用。
不同的编译系统,定义的变量在初始化前其值是不确定的。有些系统会初始化为0,而有些不是
规则8-6
指针类型变量必须初始化为NULL
规则8-7
指针不要进行复杂的逻辑或算术操作。
指针加一的偏移,通常由指针的类型确定,如果通过复杂的逻辑或算术操作,则指针的位置就很难确定
规则8-8
如果指针类型明确不会改变,应该强制为const类型的指针,以加强编译器的检查
可以防止不必要的类型转换错误。
建议8-9
C++程序中,分配内存使用new和delete,而不使用malloc和free。
new和delete操作由编译器决定具体分配和释放内存的大小,相对于malloc和firee更为高级
9. 可测试性
规则9-1
在同一项目组或产品组内,为准备集成测试和系统联调,要有一套统一的调测开关及相应信息输出函数,并且要有详细的说明统一的调试接口和输出函数由模块设计和测试人员根据项目特性统一制订,由项目系统人员统一纳入系统设计中
规则9-2
在同一个项目组或产品组内,调测打印出的信息串要有统一的格式。信息串中应当包含所在的模块名(或源文件名) 及行号等信息。
规则9-3
在编写代码之前,应预先设计好程序调试与测试的方法和手段,并设计好各种调测开关及相应测试代码(如打印函数等)
程序的调试与测试是软件生存周期中非常重要的一个阶段,如何对软件进行较全面、高效率的测试并尽可能地找出软件中的错误就成为非常关键的问题。因此在编写源代码之前,除了要有一套比较完善的测试计划外,还应设计出一系列测试代码作为手段,为单元测试、集成测试及系统联调提供方便
10.断言与错误外理
规则10-1
整个软件系统应该采用统一的断言。如果系统不提供断言,则应该自己构造一个统一的断言供编程时使用。
规则10-2
指向指针的指针及更多级的指针必须逐级检查
规则10-3
正式软件产品中应把断言及其它调测代码去掉 (即把有关的调测开关关掉 ) ,加快软件运行速度。