编程规范
本文地址:http://tongxinmao.com/Article/Detail/id/117
1、 排版
1.1 程序块要采用缩进风格编写,缩进的空格数为4个。
1.2 不允许把多个短语句写在一行中,即一行只写一条语句。
1.3 一行代码只做一件事,即不要用一行语句定义多个变量,如int width,height;
1.4 if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。
1.5 程序块的分界符(如C/C++语言的大括号‘{’和‘}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。
1.6 "!"、"~"、"++"、"--"、"&"(地址运算符)等单目操作符前后不加空格。
1.7 “[ ]”、“。”、“->”等操作符前后不加空格。
1.8 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符等二元操作符,如= += && >> ^ % * || >= 等,前后要加空格。
1.9 if、for、while、switch等与后面的括号间应加空格,使关键字更为突出、明显。
1.10 函数名后直接跟左括号,以区别关键字。
1.11 “(”向后紧跟,“)”、“,”、“;”向前紧跟,不留空格。
1.12 “,”之后要留空格,如function(x, y, z),for语句中的“;”后要留空格。
1.13 * 和 & 操作符靠近变量名,如char *name;
1.14 一行程序以小于80字符为宜,不要写得过长。
1.15 循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首。
示例:
if ((taskno < max_act_task_number)
&& (n7stat_stat_item_valid (stat_item)))
{
... // program code
}
1.16 类中把public类型的函数写在前面,把private类型的数据写在后面。
2、 注释
2.1 注释的原则是有助于对程序的阅读理解,在该加的地方都加,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。
2.2 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
2.3 避免在注释中使用缩写,在使用缩写时或之前,应对缩写进行必要的说明。
2.4 注释的内容要清楚明了,含义准确,防止二义性。 错误的注释不但无益反而有害。
2.5 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。
2.6 对于变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其含义。
2.7 全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。
2.8 对重要的条件分支、循环语句等必须编写注释。(这些语句往往是程序实现某一特定功能的关键)
2.9 对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进入下一个case处理,必须进行注释表明程序编写者的意图,防止无故遗漏break语句
2.10 当代码存在多重嵌套时,在程序块的结束行右方加注释标记,以表明某程序块的结束,这样做可以使代码更清晰,更便于阅读。
示例:
if (...)
{
// program code
while (index < MAX_INDEX)
{
// program code
} /* end of while (index < MAX_INDEX) */ // 指明该条while语句结束
} /* end of if (...)*/ // 指明是哪条if语句结束
2.11 注释应考虑程序易读及外观排版的因素,出于对维护人员的考虑,建议使用中文,除非能用非常流利准确的英文表达
2.12
2.13 说明性文件(如头文件.h文件、.inc文件、.def文件等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等。
示例:
/*************************************************
Copyright (C), 广州博纳信息技术有限公司
File name: // 文件名
Author: // 作者
Version:// 版本
Date:// 完成日期
Description: //
说明性文件(如头文件.h文件、.inc文件、.def文件等):文件完成的主要功能,与其他模块或函数的接口,输出值、取值范围、含义及参数间的控制、顺序、独立或依赖等关系
源文件:模块目的/功能
Others: // 其它内容的说明
Function List: //主要函数(供其他模块调用)列表,包括函数名及功能简要说明
History: // 修改历史记录列表,包括修改日期、修改者及修改内容简述
Date:
Author:
Modification:
*************************************************/
2.14 函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)
示例:
/*************************************************
Function: // 函数名称
Description: // 函数功能、性能等的描述
Calls: // 被本函数调用的函数清单
Called By: // 调用本函数的函数清单
Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序)
Table Updated: // 被修改的表(此项仅对于牵扯到数据库操作的程序)
Input: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系。
Output: // 对输出参数的说明。
Return: // 函数返回值的说明
Others: // 其它说明
*************************************************/
3、 标识符命名
3.1 类名和函数名用大写字母开头的单词,变量和参数用小写字母开头的单词,常量全用大写字母,用下划线分割单词。
3.2 静态变量前加前缀s_(表示static),全局变量加前缀g_(表示global).
3.3 类的数据成员前加前缀m_(表示member)
3.4 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。(较短的单词可通过去掉“元音”形成缩写,如temp=>tmp;较长的单词可取单词的头几个字母如increment=>inc;一些单词有大家公认的缩写,如message=>msg)
3.5 在符合项目组命名规则的前提下,自己特有的命名风格,要自始至终保持一致,不可来回变化。
3.6 对于变量命名,禁止取单个字符(如i、j、k...),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但i、j、k作局部循环变量是允许的。
3.7 命名规范必须与所使用的系统风格保持一致,LINUX下采用全小写加下划线的风格或大小写混排的方式。
3.8 除非必要,不要用数字或较奇怪的字符来定义标识符,如value1,value2
3.9 对接口部分的标识符(变量、结构、函数及常量)加上“模块”标识,防止冲突。
3.10 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
Add/remove begin/end create/destroy insert/delete first/last put/get
Increment/decrement lock/unlock start/stop min/max previout/next
Source/destination show/hide send/receive open/close get/set
4、 可读性
4.1 注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。
4.1.1.1 如:word = (high << 8) | low (1)
4.2 涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。
示例:
led=0;改写为
#define LED_ON 0
led=LED_ON;
4.3 不要使用难懂的技巧性很高的语句,除非很有必要时。
示例:
*stat_poi ++ += 1; // 应改写为以下两句语句:
*stat_poi += 1;
stat_poi++;
5、 变量、结构
5.1 尽量避免使用全局变量,以减少模块间的耦合
5.2 定义公共变量时应对其含义、作用及取值范围进行注释说明
5.3 明确公共变量与操作此公共变量的函数或过程的关系,如访问、修改及创建等。
5.4 防止局部变量与公共变量同名(虽然语法上没有错误)。
5.5 严禁使用未经初始化的变量(尤其是指针)作为右值。尽可能在定义时初始化。
5.6 使用标准的、可移植的数据类型,尽量不要使用与具体硬件或软件环境关系密切的变量。
5.7 结构的功能要单一,是针对一种事务的抽象。不要设计面面俱到、非常灵活的数据结构,结构中的各元素应代表同一事务的不同侧面,若两个结构间关系较复杂、密切,那么应合为一个结构。
5.8 仔细设计结构中元素的布局与排列顺序,使结构容易理解、节省占用空间,并减少引起误用现象。
5.9 结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地(如预留一些空间等)。
5.10 尽量减少没有必要的数据类型默认转换与强制转换。
5.11 当声明用于分布式环境或不同CPU间通信环境的数据结构时,必须考虑机器的字节顺序、使用的位域及字节对齐等问题 。
6、 函数、过程
6.1 编写可重入函数时,应注意局部变量的使用(不应使用static局部变量)
6.2 编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。
6.3 函数的调用者负责对接口函数参数的合法性检查。
6.4 函数的规模尽量限制在200行以内(不包括注释和空格行)。
6.5 一个函数仅完成一项功能, 不要设计多用途面面俱到的函数。
6.6 为简单功能编写函数,虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。对于功能不明确且较小的函数,特别是仅有一个上级函数调用它时,应考虑把它合并到上级函数中,而不必单独存在
示例:
value = ( a > b ) ? a : b ;// 改写为
int max (int a, int b)
{
return ((a > b) ? a : b);
}
6.7 函数名使用动宾词组并应准确描述函数的功能,类中的方法只使用动词。
6.8 函数参数名要恰当,顺序要合理(目的参数在前,源参数在后)。
示例:void StringCopy(char *strDestination, char *strSource);
6.9 如果函数参数是指针,且仅作输入用,应在类型前加const,以防止被意外修改。
6.10 如果输入参数以值传递的方式传递对象,则宜用const & 方式传递以省去临时对象的构造和析构过程,从而提高效率。
6.11 函数没有参数时,也不能省略”void”。
6.12 在函数体的入口处对参数进行有效性检查(可使用断言assert)。
6.13 不要省略返回值的类型,如果没有返回值应声明为void类型。
6.14 设计高扇入、合理扇出(小于7)的函数。扇出是指一个函数直接调用(控制)其它函数的数目,而扇入是指有多少上级函数调用它。扇出过大,表明函数过分复杂,而扇出过小,如总是1,表明函数的调用层次可能过多。扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。
6.15 减少函数本身或函数间(A->B->C->A)的递归调用。
6.16 对于提供了返回值的函数,在引用时最好使用其返回值。
6.17 当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替。
7、 质量保证
7.2 代码质量保证优先原则
7.3 只引用属于自己的存贮空间。
7.4 防止引用已经释放的内存空间。
7.5 过程/函数中分配的内存,在过程/函数退出之前要释放。
7.6 过程/函数中申请的句柄,在过程/函数退出之前要关闭。
7.7 防止内存操作越界(主要是指对数组、指针、内存地址等的操作)。
7.8 严禁随意更改其它模块或系统的有关设置和配置。
7.9 有可能的话,if语句尽量加上else分支,对没有else分支的语句要小心对待;switch语句必须有default分支。
7.10 goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。
7.11 不使用与硬件或操作系统关系很大的语句,而使用建议的标准语句,以提高软件的可移植性和可重用性。
7.12 除非为了满足特殊需求,避免使用嵌入式汇编。(影响可移植性)
7.13 使用变量时要注意其边界值的情况,如C中char有效值范围为-128到127.
7.14 用宏定义表达式时,要使用完备的括号。
7.15 将宏所定义的多条表达式放在大括号中.\
7.16 使用宏时,不允许参数发生变化
示例:
#define SQUARE( a ) ((a) * (a))
int a = 5;
int b;
b = SQUARE( a++ ); // 结果:a = 7,即执行了两次增1。
正确的用法是:
b = SQUARE( a );
a++; // 结果:a = 6,即只执行了一次增1。
8、 程序效率
8.1 在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效率。
8.2 局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响。
8.3 循环体内工作量最小化。
8.4 要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数。
8.5 在多重循环中,应将最忙的循环放在最内层。
8.6 尽量减少循环嵌套层次。
8.7 尽量用乘法或其它方法代替除法,特别是浮点运算中的除法。
9、 可移植性
9.1 遵循标准C语言规范, 使用标准库函数
9.2 尽量使用C语言而不是C++实现
9.3 尽可能使所写的程序适用于所有的编译程序, 假如你的手册提醒你某种功能或某个函数是你的编译程序或某些编译程序所特有的,你就应该谨慎地使用它
9.4 把不可移植的代码分离出来, 把其中不可移植的代码分离到一些独立的“.c”文件, 假如只在一些小的程序段中存在可移植性问题,你可以使用#ifdef预处理指令
9.5 把功能代码实现为一个独立的函数而不是写在图形界面的事件响应函数中
9.6 分层设计,隔离平台相关的代码
9.7 尽量不要使用C/C++标准里没有明确规定的特性