|
编程规范与范例(2) 〖文章转载或出处〗≡中国电子技术信息网≡ 网址:www.CETINet.com 编程规范与范例(2)
目 录 1 排版 6 2 注释 11 3 标识符命名 18 4 可读性 20 5 变量、结构 22 6 函数、过程 28 7 可测性 36 8 程序效率 40 9 质量保证 44 10 代码编辑、编译、审查 50 11 代码测试、维护 52 12 宏 53
3 标识符命名 ¹ 3-1:标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。 说明:较短的单词可通过去掉“元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写。 示例:如下单词的缩写能够被大家基本认可。 temp 可缩写为 tmp ; flag 可缩写为 flg ; statistic 可缩写为 stat ; increment 可缩写为 inc ; message 可缩写为 msg ; ¹ 3-2:命名中若使用特殊约定或缩写,则要有注释说明。 说明:应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。 ¹ 3-3:自己特有的命名风格,要自始至终保持一致,不可来回变化。 说明:个人的命名风格,在符合所在项目组或产品组的命名规则的前提下,才可使用。(即命名规则中没有规定到的地方才可有个人命名风格)。 ¹ 3-4:对于变量命名,禁止取单个字符(如i、j、k...),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但i、j、k作局部循环变量是允许的。 说明:变量,尤其是局部变量,如果用单个字符表示,很容易敲错(如i写成j),而编译时又检查不出来,有可能为了这个小小的错误而花费大量的查错时间。 示例:下面所示的局部变量名的定义方法可以借鉴。 int liv_Width 其变量名解释如下: l 局部变量(Local) (其它:g 全局变量(Global)...) i 数据类型(Interger) v 变量(Variable) (其它:c 常量(Const)...) Width 变量含义 这样可以防止局部变量与全局变量重名。 ¹ 3-5:命名规范必须与所使用的系统风格保持一致,并在同一项目中统一,比如采用UNIX的全小写加下划线的风格或大小写混排的方式,不要使用大小写与下划线混排的方式,用作特殊标识如标识成员变量或全局变量的m_和g_,其后加上大小写混排的方式是允许的。 示例: Add_User不允许,add_user、AddUser、m_AddUser允许。 ½ 3-1:除非必要,不要用数字或较奇怪的字符来定义标识符。 示例:如下命名,使人产生疑惑。 #define _EXAMPLE_0_TEST_ #define _EXAMPLE_1_TEST_ void set_sls00( BYTE sls );
应改为有意义的单词命名 #define _EXAMPLE_UNIT_TEST_ #define _EXAMPLE_ASSERT_TEST_ void set_udt_msg_sls( BYTE sls ); ½ 3-2:在同一软件产品内,应规划好接口部分标识符(变量、结构、函数及常量)的命名,防止编译、链接时产生冲突。 说明:对接口部分的标识符应该有更严格限制,防止冲突。如可规定接口部分的变量与常量之前加上“模块”标识等。 ½ 3-3:用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。 说明:下面是一些在软件中常用的反义词组。 add / remove begin / end create / destroy insert / delete first / last get / release increment / decrement put / get add / delete lock / unlock open / close min / max old / new start / stop next / previous source / target show / hide send / receive source / destination cut / paste up / down 示例: int min_sum; int max_sum; int add_user( BYTE *user_name ); int delete_user( BYTE *user_name ); ½ 3-4:除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。 4 可读性 ¹ 4-1:注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。 说明:防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。 示例:下列语句中的表达式 word = (high << 8) | low (1) if ((a | b) && (a & c)) (2) if ((a | b) < (c & d)) (3) 如果书写为 high << 8 | low a | b && a & c a | b < c & d 由于 high << 8 | low = ( high << 8) | low, a | b && a & c = (a | b) && (a & c), (1)(2)不会出错,但语句不易理解; a | b < c & d = a | (b < c) & d,(3)造成了判断条件出错。 ¹ 4-2:避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。 示例:如下的程序可读性差。 if (Trunk[index].trunk_state == 0) { Trunk[index].trunk_state = 1; ... // program code }
应改为如下形式。 #define TRUNK_IDLE 0 #define TRUNK_BUSY 1
if (Trunk[index].trunk_state == TRUNK_IDLE) { Trunk[index].trunk_state = TRUNK_BUSY; ... // program code } ½ 4-1:源程序中关系较为紧密的代码应尽可能相邻。 说明:便于程序阅读和查找。 示例:以下代码布局不太合理。 rect.length = 10; char_poi = str; rect.width = 5;
若按如下形式书写,可能更清晰一些。 rect.length = 10; rect.width = 5; // 矩形的长与宽关系较密切,放在一起。 char_poi = str; ½ 4-2:不要使用难懂的技巧性很高的语句,除非很有必要时。 说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。 示例:如下表达式,考虑不周就可能出问题,也较难理解。 * stat_poi ++ += 1;
* ++ stat_poi += 1;
应分别改为如下。 *stat_poi += 1; stat_poi++; // 此二语句功能相当于“ * stat_poi ++ += 1; ”
++ stat_poi; *stat_poi += 1; // 此二语句功能相当于“ * ++ stat_poi += 1; ” 5 变量、结构 ¹ 5-1:去掉没必要的公共变量。 说明:公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以降低模块间的耦合度。 ¹ 5-2:仔细定义并明确公共变量的含义、作用、取值范围及公共变量间的关系。 说明:在对变量声明的同时,应对其含义、作用及取值范围进行注释说明,同时若有必要还应说明与其它变量的关系。 ¹ 5-3:明确公共变量与操作此公共变量的函数或过程的关系,如访问、修改及创建等。 说明:明确过程操作变量的关系后,将有利于程序的进一步优化、单元测试、系统联调以及代码维护等。这种关系的说明可在注释或文档中描述。 示例:在源文件中,可按如下注释形式说明。 RELATION System_Init Input_Rec Print_Rec Stat_Score Student Create Modify Access Access Score Create Modify Access Access, Modify
注:RELATION为操作关系;System_Init、Input_Rec、Print_Rec、Stat_Score为四个不同的函数;Student、Score为两个全局变量;Create表示创建,Modify表示修改,Access表示访问。 其中,函数Input_Rec、Stat_Score都可修改变量Score,故此变量将引起函数间较大的耦合,并可能增加代码测试、维护的难度。 ¹ 5-4:当向公共变量传递数据时,要十分小心,防止赋与不合理的值或越界等现象发生。 说明:对公共变量赋值时,若有必要应进行合法性检查,以提高代码的可靠性、稳定性。 ¹ 5-5:防止局部变量与公共变量同名。 说明:若使用了较好的命名规则,那么此问题可自动消除。 ¹ 5-6:严禁使用未经初始化的变量作为右值。 说明:特别是在C/C++中引用未经赋值的指针,经常会引起系统崩溃。 ½ 5-1:构造仅有一个模块或函数可以修改、创建,而其余有关模块或函数只访问的公共变量,防止多个不同模块或函数都可以修改、创建同一公共变量的现象。 说明:降低公共变量耦合度。 ½ 5-2:使用严格形式定义的、可移植的数据类型,尽量不要使用与具体硬件或软件环境关系密切的变量。 说明:使用标准的数据类型,有利于程序的移植。 示例:如下例子(在DOS下BC3.1环境中),在移植时可能产生问题。 void main() { register int index; // 寄存器变量
_AX = 0x4000; // _AX是BC3.1提供的寄存器“伪变量” ... // program code } ½ 5-3:结构的功能要单一,是针对一种事务的抽象。 说明:设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系或关系很弱的不同事务的元素放到同一结构中。 示例:如下结构不太清晰、合理。 typedef struct STUDENT_STRU { unsigned char name[8]; /* student's name */ unsigned char age; /* student's age */ unsigned char sex; /* student's sex, as follows */ /* 0 - FEMALE; 1 - MALE */ unsigned char teacher_name[8]; /* the student teacher's name */ unisgned char teacher_sex; /* his teacher sex */ } STUDENT;
若改为如下,可能更合理些。 typedef struct TEACHER_STRU { unsigned char name[8]; /* teacher name */ unisgned char sex; /* teacher sex, as follows */ /* 0 - FEMALE; 1 - MALE */ } TEACHER;
typedef struct STUDENT_STRU { unsigned char name[8]; /* student's name */ unsigned char age; /* student's age */ unsigned char sex; /* student's sex, as follows */ /* 0 - FEMALE; 1 - MALE */ unsigned int teacher_ind; /* his teacher index */ } STUDENT; ½ 5-4:不要设计面面俱到、非常灵活的数据结构。 说明:面面俱到、灵活的数据结构反而容易引起误解和操作困难。 ½ 5-5:不同结构间的关系不要过于复杂。 说明:若两个结构间关系较复杂、密切,那么应合为一个结构。 示例:如下两个结构的构造不合理。 typedef struct PERSON_ONE_STRU { unsigned char name[8]; unsigned char addr[40]; unsigned char sex; unsigned char city[15]; } PERSON_ONE;
typedef struct PERSON_TWO_STRU { unsigned char name[8]; unsigned char age; unsigned char tel; } PERSON_TWO;
由于两个结构都是描述同一事物的,那么不如合成一个结构。 typedef struct PERSON_STRU { unsigned char name[8]; unsigned char age; unsigned char sex; unsigned char addr[40]; unsigned char city[15]; unsigned char tel; } PERSON; ½ 5-6:结构中元素的个数应适中。若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数。 说明:增加结构的可理解性、可操作性和可维护性。 示例:假如认为如上的_PERSON结构元素过多,那么可如下对之划分。 typedef struct PERSON_BASE_INFO_STRU { unsigned char name[8]; unsigned char age; unsigned char sex; } PERSON_BASE_INFO;
typedef struct PERSON_ADDRESS_STRU { unsigned char addr[40]; unsigned char city[15]; unsigned char tel; } PERSON_ADDRESS;
typedef struct PERSON_STRU { PERSON_BASE_INFO person_base; PERSON_ADDRESS person_addr; } PERSON; ½ 5-7:仔细设计结构中元素的布局与排列顺序,使结构容易理解、节省占用空间,并减少引起误用现象。 说明:合理排列结构中元素顺序,可节省空间并增加可理解性。 示例:如下结构中的位域排列,将占较大空间,可读性也稍差。 typedef struct EXAMPLE_STRU { unsigned int valid: 1; PERSON person; unsigned int set_flg: 1; } EXAMPLE;
若改成如下形式,不仅可节省1字节空间,可读性也变好了。 typedef struct EXAMPLE_STRU { unsigned int valid: 1; unsigned int set_flg: 1; PERSON person ; } EXAMPLE; ½ 5-8:结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地(如预留一些空间等)。 说明:软件向前兼容的特性,是软件产品是否成功的重要标志之一。如果要想使产品具有较好的前向兼容,那么在产品设计之初就应为以后版本升级保留一定余地,并且在产品升级时必须考虑前一版本的各种特性。 ½ 5-9:留心具体语言及编译器处理不同数据类型的原则及有关细节。 说明:如在C语言中,static局部变量将在内存“数据区”中生成,而非static局部变量将在“堆栈”中生成。这些细节对程序质量的保证非常重要。 ½ 5-10:编程时,要注意数据类型的强制转换。 说明:当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。 ½ 5-11:对编译系统默认的数据类型转换,也要有充分的认识。 示例:如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。 char chr; unsigned short int exam;
chr = -1; exam = chr; // 编译器不产生告警,此时exam为0xFFFF。 ½ 5-12:尽量减少没有必要的数据类型默认转换与强制转换。 ½ 5-13:合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转换。 ½ 5-14:对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。注意其命名方式在同一产品中的统一。 说明:使用自定义类型,可以弥补编程语言提供类型少、信息量不足的缺点,并能使程序清晰、简洁。 示例:可参考如下方式声明自定义数据类型。
下面的声明可使数据类型的使用简洁、明了。 typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned int DWORD;
下面的声明可使数据类型具有更丰富的含义。 typedef float DISTANCE; typedef float SCORE; ½ 5-15:当声明用于分布式环境或不同CPU间通信环境的数据结构时,必须考虑机器的字节顺序、使用的位域及字节对齐等问题 。 说明:比如Intel CPU与68360 CPU,在处理位域及整数时,其在内存存放的“顺序”正好相反。 示例:假如有如下短整数及结构。 unsigned short int exam; typedef struct EXAM_BIT_STRU { /* Intel 68360 */ unsigned int A1: 1; /* bit 0 7 */ unsigned int A2: 1; /* bit 1 6 */ unsigned int A3: 1; /* bit 2 5 */ } EXAM_BIT;
如下是Intel CPU生成短整数及位域的方式。 内存: 0 1 2 ... (从低到高,以字节为单位) exam exam低字节 exam高字节
内存: 0 bit 1 bit 2 bit ... (字节的各“位”) EXAM_BIT A1 A2 A3
如下是68360 CPU生成短整数及位域的方式。 内存: 0 1 2 ... (从低到高,以字节为单位) exam exam高字节 exam低字节
内存: 7 bit 6 bit 5 bit ... (字节的各“位”) EXAM_BIT A1 A2 A3
说明:在对齐方式下,CPU的运行效率要快得多。 示例:如下图,当一个long型数(如图中long1)在内存中的位置正好与内存的字边界对齐时,CPU存取这个数只需访问一次内存,而当一个long型数(如图中的long2)在内存中的位置跨越了字边界时,CPU存取这个数就需要多次访问内存,如i960cx访问这样的数需读内存三次(一个BYTE、一个SHORT、一个BYTE,由CPU的微代码执行,对软件透明),所有对齐方式下CPU的运行效率明显快多了。 1 8 16 24 32 ------- ------- ------- ------- | long1 | long1 | long1 | long1 | ------- ------- ------- ------- | | | | long2 | ------- ------- ------- -------- | long2 | long2 | long2 | | ------- ------- ------- -------- | ....
|