华为软件编程轨范和实例 目 录 一 排字
六
二 诠释
11
三 标识符定名
18
四 可读性
20
五 变量、构造
22
六 函数、进程
28
七 可测性
36
八 程序效率
40
九 质量保证
44
十 代码编者、编译、考察
50
11 代码测试、维护
52
12 宏
53
一 排字
¹一-一:程序块要采取缩进作风编撰,缩进的空格数为4个。
说明:关于由开发工具自动生成的代码可以有不一致。
¹一-二:相对独力的程序块其间、变量说明以后务须加空行。
示范:如次例证不符合轨范。
if (!valid_ni(ni))
{
... // program code
}
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
当如次书写
if (!valid_ni(ni))
{
... // program code
}
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
¹一-三:较长的话语(>80字符)要分为多行书写,长表达式要在低优先级操作符处区划新行,操作符放在新行之首,区划出的新行要开展适当的缩进,使排字齐整,话语可读。
示范:
perm_count_msg.head.len = NO七_TO_STAT_PERM_COUNT_LEN
+ STAT_SIZE_PER_FRAM * sizeof( _UL );
act_task_table[frame_id * STAT_TASK_CHECK_NUMBER + index].occupied
= stat_poi[index].occupied;
act_task_table[taskno].duration_true_or_false
= SYS_get_sccp_statistic_state( stat_item );
report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)
&& (n七stat_stat_item_valid (stat_item))
&& (act_task_table[taskno].result_data != 零));
¹一-四:循环、判断等话语中若有较长的表达式或话语,则要进展顺应的区划,长表达式要在低优先级操作符处区划新行,操作符放在新行之首。
示范:
if ((taskno < max_act_task_number)
&& (n七stat_stat_item_valid (stat_item)))
{
... // program code
}
for (i = 零, j = 零; (i < BufferKeyword[word_index].word_length)
&& (j < NewKeyword.word_length); i++, j++)
{
... // program code
}
for (i = 零, j = 零;
(i < first_word_length) && (j < second_word_length);
i++, j++)
{
... // program code
}
¹一-五:若函数或过程中的参数较长,则要开展适当的区划。
示范:
n七stat_str_compare((BYTE *) & stat_object,
(BYTE *) & (act_task_table[taskno].stat_object),
sizeof (_STAT_OBJECT));
n七stat_flash_act_duration( stat_item, frame_id *STAT_TASK_CHECK_NUMBER
+ index, stat_object );
¹一-六:不允许把多个词组句写在一起中,即一起只写一条话语。
示范:如次事例不符合轨范。
rect.length = 零; rect.width = 零;
当如次书写
rect.length = 零;
rect.width = 零;
¹一-七:if、for、do、while、case、switch、default等话语自占一起,且if、for、do、while等话语的施行话语一部分无论是几多都要加括号{}。
示范:如次例证不符合轨范。
if (pUserCR == NULL) return;
当如次书写:
if (pUserCR == NULL)
{
return;
}
¹一-八:对齐只运用空格键,不运用TAB键。
说明:省得用不同的编辑器阅读程序时,因TAB键所设立的空格数额不同而促成程序格局不整齐,不用应用BC作为编辑器合版本,由于BC能自动将8个空格成为一个TAB键,因而运用BC合进的版本大多会将缩进骚乱。
¹一-九:函数或历程的开始、构造的定义及循环、判断等话语中的代码都要采取缩进作派,case话语停的景况处置话语也要遵照话语缩进要求。
¹一-十:程序块的界线符(如C/C++语言的大括号‘{’和‘}’)当各独有一起而且坐落同一列,与此同时与摘引它们的话语左对齐。在函数体的开始、种的定义、构造的定义、枚举的定义以及if、for、do、while、switch、case话语中的程序都要采取上述的缩进模式。
示范:如次例证不符合轨范。
for (...) {
... // program code
}
if (...)
{
... // program code
}
void example_fun( void )
{
... // program code
}
当如次书写。
for (...)
{
... // program code
}
if (...)
{
... // program code
}
void example_fun( void )
{
... // program code
}
¹一-11:在两个之上的关键字、变量、恒量开展相当操作时,它们其间的操作符先头、以后可能前前后后要添空格;进展非相当操作时,如果是关系密切的马上操作符(如->),后不应加空格。
说明:采取这种紧密形式编纂代码的目的是使代码愈加明晰。
因为留空格所发生的明晰性是相对的,之所以,在已经十分明晰的话语中没有必要再留空格,如若话语已足够明晰则括号内侧(即左括号后边和右括号前方)不需要加空格,多重括号间不须加空格,由于在C/C++语言中括号已经是最明晰的标示了。
在长话语中,如其亟需添的空格十分多,那么应当护持通体明晰,而在局部不加空格。给操作符留空格时不用接续留两个之上空格。
示范:
(一) 逗号、分号只在后面加空格。
int a, b, c;
(二)比较操作符, 赋值操作符"="、 "+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",位域操作符"<<"、"^"等双眼操作符的前前后后加空格。
if (current_time >= MAX_TIME_VALUE)
a = b + c;
a *= 二;
a = b ^ 二;
(三)"!"、"~"、"++"、"--"、"&"(地址演算符)等单目操作符前前后后不加空格。
*p = 'a'; // 内容操作"*"与内容其间
flag = !isEmpty; // 非操作"!"与内容其间
p = &mem; // 地址操作"&" 与内容其间
i++; // "++","--"与内容其间
(四)"->"、"."前前后后不加空格。
p->id = pid; // "->"指针前前后后不加空格
(五) if、for、while、switch等与后边的括号间应加空格,使if等关键字更其突出、显然。
if (a >= b && c > d)
½一-一:一起程序以小于80字符为宜,不用写得过长。
二 诠释
¹二-一:正常情况下,源程序有效诠释量务必在20%之上。
说明:诠释的准则是有助于对程序的翻阅懂得,在该加的地方都加了,诠释不宜太多也不能太少,诠释语言务必正确、易懂、简明。
¹二-二:说明性资料(如头资料.h资料、.inc资料、.def资料、编译论说文件.cfg等)脑袋应开展诠释,诠释务须列出:版权说明、版本号、生终日期、写稿人、内容、效能、与其说它资料的关系、批改日记等,头资料的诠释中还应有函数效能简明说明。
示范:底下这段头资料的头诠释比较基准,当然,并不局限于此格式,但如上信息提议要包孕在内。
/*************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
File name: // 文件名
Author: Version: Date: // 著者、版本及完终日期
Description: // 用来详细说明此程题词件完成的重要效能,与其说他模块
// 或函数的接口,输出值、取值范畴、含意及参数间的控
// 制、顺序、独力或委以等关系
Others: // 其它内容的说明
Function List: // 重要函数列表,每条记要应包括函数名及效能简明说明
1. ....
History: // 批改历史记录列表,每条批改记要应包括批改日期、批改
// 者及批改内容简述
1. Date:
Author:
Modification:
2. ...
*************************************************/
¹二-三:源文件脑袋应进展诠释,列出:版权说明、版本号、生终日期、起草人、模块目的/效能、重要函数及其效能、批改日记等。
示范:下头这段源文件的头诠释比较基准,当然,并不局限于此格式,但如上信息提议要包孕在内。
/************************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
FileName: test.cpp
Author: Version : Date:
Description: // 模块描述
Version: // 版本信息
Function List: // 重要函数及其效能
1. -------
History: // 历史批改记要
David 96/10/12 1.0 build this moudle
***********************************************************/
说明:Description一项描述正文件的内容、效能、内部各部分其间的关系及正文件毋宁它资料关系等。History是批改历史记录列表,每条批改记要应包括批改日期、批改者及批改内容简述。
¹二-四:函数脑袋应进展诠释,列出:函数的目的/效能、输入参数、输出参数、回来值、调用关系(函数、表)等。
示范:下边这段函数的诠释比较基准,当然,并不局限于此格式,但如上信息提议要包孕在内。
/*************************************************
Function: // 函数姓名
Description: // 函数效能、性能等的描述
Calls: // 被本函数调用的函数清单
Called By: // 调用本函数的函数清单
Table Accessed: // 被访问的表(此项仅关于牵连到数据库操作的程序)
Table Updated: // 被批改的表(此项仅关于牵涉到数据库操作的程序)
Input: // 输入参数叨明,包括每个参数的做
// 用、取值说明及参数间关系。
Output: // 对输出参数的说明。
Return: // 函数回来值的说明
Others: // 其它说明
*************************************************/
¹二-五:边写代码边诠释,批改代码与此同时批改呼应的诠释,以军令状诠释与代码的一致性。不还有用的诠释要剔除。
¹二-六:诠释的内容要明白、了然,含意正确,防止诠释2义性。
说明:差错的诠释非但无益反是有害。
规约二-七:避免在诠释中应用缩写,特别是是非非惯用缩写。
说明:在应用缩写时或事先,应对缩写开展必要的说明。
¹二-八:诠释应与其说描述的代码临近,对代码的诠释应放在其上方或右边(对单条话语的诠释)紧邻位置,不可放鄙人面,如放于上方则需与其说上头的代码用空行隔开。
示范:如次例证不符合轨范。
例一:
/* get replicate sub system index and net indicator */
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
例二:
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
/* get replicate sub system index and net indicator */
当如次书写
/* get replicate sub system index and net indicator */
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
¹二-九:关于全部有物理含意的变量、恒量,如其其定名不是充分自诠释的,在宣言时都必须加以诠释,说明其物理含意。变量、恒量、宏的诠释应放在其上方紧邻位置或右边。
示范:
/* active statistic task number */
#define MAX_ACT_TASK_NUMBER 1000
#define MAX_ACT_TASK_NUMBER 1000 /* active statistic task number */
¹二-十:数据结构宣言(包括数组、构造、种、枚举等),如其其定名不是充分自诠释的,务必加以诠释。对数据结构的诠释应放在其上方紧邻位置,不可放鄙人面;对构造中的每个域的诠释放在此域的右首。
示范:可按如次模式说明枚举/数据/联合结构。
/* sccp interface with sccp user primitive message name */
enum SCCP_USER_PRIMITIVE
{
N_UNITDATA_IND, /* sccp notify sccp user unit data come */
N_NOTICE_IND, /* sccp notify user the No.七 network can not */
/* transmission this message */
N_UNITDATA_REQ, /* sccp user's unit data transmission request*/
};
¹二-11:全局变量要有较详细的诠释,包括对其效能、取值范畴、哪些函数或历程存取它以及存取时注意事项等的说明。
示范:
/* The ErrorCode when SCCP translate */
/* Global Title failure, as follows */ // 变量功用、含意
/* 零 - SUCCESS 一 - GT Table error */
/* 二 - GT error Others - no use */ // 变量取值范畴
/* only function SCCPTranslate() in */
/* this modual can modify it, and other */
/* module can visit it through call */
/* the function GetGTTransErrorCode() */ // 使用方法
BYTE g_GTTranErrorCode;
¹二-12:诠释与所描述内容开展一样的缩排。
说明:可使程序排字齐整,并便利诠释的阅览与了解。
示范:如次例证,排字不整齐,翻阅稍感不便。
void example_fun( void )
{
/* code one comments */
CodeBlock One
/* code two comments */
CodeBlock Two
}
当改成如次格局。
void example_fun( void )
{
/* code one comments */
CodeBlock One
/* code two comments */
CodeBlock Two
}
¹二-13:将诠释与其说上边的代码用空行隔开。
示范:如次例证,显得代码过于紧凑。
/* code one comments */
program code one
/* code two comments */
program code two
当如次书写
/* code one comments */
program code one
/* code two comments */
program code two
¹二-14:对变量的定义和旁支话语(条件旁支、循环话语等)务须编纂诠释。
说明:这些话语往往是程序兑现某一特定效能的要害,至于维护人员来说,很好的诠释相助更好的懂得程序,有时候甚或优于看设计文档。
¹二-15:关于switch话语停的case话语,如其由于特殊情况亟需处置完一个case后进去下一个case处置,务须在该case话语处理完、下一个case话语前加上明确的诠释。
说明:这么比较清楚程序编辑者的企图,有效防止平白无故疏漏break话语。
示范(注意斜体加粗一部分):
case CMD_UP:
ProcessUp();
break;
case CMD_DOWN:
ProcessDown();
break;
case CMD_FWD:
ProcessFwd();
if (...)
{
...
break;
}
else
{
ProcessCFW_B(); // now jump into case CMD_A
}
case CMD_A:
ProcessA();
break;
case CMD_B:
ProcessB();
break;
case CMD_C:
ProcessC();
break;
case CMD_D:
ProcessD();
break;
...
½二-一:避免在一起代码或表达式的当中安插诠释。
说明:除非必要,不应在代码或表述当中安插诠释,不然简单使代码可理解性变差。
½二-二:经过对函数或进程、变量、构造等准确的定名以及合理地部门代码的构造,使代码变为自诠释的。
说明:明晰正确的函数、变量等的定名,可增加代码可读性,并减小用不着的诠释。
½二-三:在代码的效能、企图层次长进行诠释,提供有用、额外的信息。
说明:诠释的目的是解释代码的目的、效能和采取的步骤,提供代码之外的信息,相助读者了解代码,防止没必要的反复诠释信息。
示范:如次诠释意思不大。
/* if receive_flag is TRUE */
if (receive_flag)
而如次的诠释则付出了额外有用的信息。
/* if mtp receive a message from links */
if (receive_flag)
½二-四:在程序块的完结行右边加诠释标记,以表明某程序块的完结。
说明:现代码段较长,特别是多重嵌套时,这么干可以使代码更明晰,更易于阅览。
示范:参看如次例证。
if (...)
{
// program code
while (index < MAX_INDEX)
{
// program code
} /* end of while (index < MAX_INDEX) */ // 点明该条while话语完了
} /* end of if (...)*/ // 点明是哪条if话语完了
½二-五:诠释格式尽可能一致,提议运用“/* …… */”。
½二-六:诠释应试虑程序易读及外观排字的要素,运用的语言要是中、英兼具的,提议多运用汉语言,除非能用十分流畅正确的英文表述。
说明:诠释语言不一致,影响程序易读性和外观排字,鉴于对维护人员的思忖,提议应用汉语言。
三 标识符定名
¹三-一:标识符的定名要明晰、了然,有明确含意,与此同时应用完整的单纯词或大伙儿根本可以懂得的缩写,避免使人产生误解。
说明:较短的单纯词可通过去掉“元音”形成缩写;较长的单纯词可取单纯词的头几个字母形成缩写;一些单纯词有大伙儿一致承认的缩写。
示范:如次单纯词的缩写能够被大伙根本认同。
temp 可缩写为 tmp ;
flag 可缩写为 flg ;
statistic 可缩写为 stat ;
increment 可缩写为 inc ;
message 可缩写为 msg ;
¹三-二:定名中若应用非一般约定或缩写,则要有诠释说明。
说明:应该在源文件的开始之处,对资料中所施用的缩写或约定,特别是非一般的缩写,开展必要的诠释说明。
¹三-三:自各儿特有的定名作风,要始终如一保持一致,不可来回变更。
说明:个人的定名作风,在吻合所在项目组或产品组的定名守则的前提下,才可应用。(即定名守则中没规定到的地刚才可有个民命名作派)。
¹三-四:关于变量定名,禁止取单个字符(如i、j、k...),提议除开要有具体含意外,还能表明其变量部类、数据门类等,但i、j、k做局部循环变量是容许的。
说明:变量,尤其是局部变量,如其用单个字符示意,很简略敲错(如i写成j),而编译时又稽查不出来,有或许为了这个很小差错而开销大量的查错时间。
示范:下边所示的局部变量名的定义步骤可以龟鉴。
int liv_Width
其变量名解释如次:
l 局部变量(Local) (其它:g 全局变量(Global)...)
i 数据部类(Interger)
v 变量(Variable) (其它:c 恒量(Const)...)
Width 变量含意
这么可以防止局部变量与全局变量重名。
¹三-五:定名轨范务必与所应用的系统作派保持一致,并在同一项目中一致,例如采取UNIX的全小写加下划线的作派或大小写混排的形式,不用施用大小写与下划线混排的形式,视作非一般标识如标识成员变量或全局变量的m_和g_,然后加上大小写混排的模式是容许的。
示范: Add_User不允许,add_user、AddUser、m_AddUser容许。
½三-一:除非必要,不用用数目字或较比奇怪的字符来定义标识符。
示范:如次定名,使人发生困惑。
#define _EXAMPLE_零_TEST_
#define _EXAMPLE_一_TEST_
void set_sls00( BYTE sls );
当改成故意义的单纯词定名
#define _EXAMPLE_UNIT_TEST_
#define _EXAMPLE_ASSERT_TEST_
void set_udt_msg_sls( BYTE sls );
½三-二:在同一软件产品内,当规划好接口一部分标识符(变量、构造、函数及恒量)的定名,防止编译、链接时发生摩擦。
说明:衔接口一部分的标识符应该有更严厉限制,防止摩擦。如可规定接口一部分的变量与恒量头里加上“模块”标识等。
½三-三:用准确的反义短语定名具有互斥意思的变量或相反动作的函数等。
说明:底下是一些在软件中惯用的反义短语。
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 );
½三-四:除开编译开关/头资料等非一般施用,当避免施用_EXAMPLE_TEST_等等以次划线开始和扫尾的定义。
四 可读性
¹四-一:注意演算符的优先级,并用括号明确表达式的操作顺序,避免施用默许优先级。
说明:防止阅读程序时产生误解,防止因默许的优先级与设计思维不符而以致程序疏失。
示范:下列话语中的表达式
word = (high << | low (一)
if ((a | b) && (a & c)) (二)
if ((a | b) < (c & d)) (三)
如若书写为
high << 八 | low
a | b && a & c
a | b < c & d
因为
high << 八 | low = ( high << | low,
a | b && a & c = (a | b) && (a & c),
(一)(二)不会疏失,但话语不易懂得;
a | b < c & d = a | (b < c) & d,(三)造成了判断条件疏失。
¹四-二:避免施用不易了解的数目字,用故意义的标识来代替。牵系物理状态或许带有物理意思的恒量,不应直接运用数目字,务须用故意义的枚举或宏来替代。
示范:如次的程序可读性差。
if (Trunk[index].trunk_state == 零)
{
Trunk[index].trunk_state = 一;
... // program code
}
当改成如次模式。
#define TRUNK_IDLE 零
#define TRUNK_BUSY 一
if (Trunk[index].trunk_state == TRUNK_IDLE)
{
Trunk[index].trunk_state = TRUNK_BUSY;
... // program code
}
½四-一:源程序中关系较为松弛的代码应尽量邻近。
说明:易于程序翻阅和查寻。
示范:以次代码格局不太合理。
rect.length = 十;
char_poi = str;
rect.width = 五;
若按如次方式书写,或许更明晰一些。
rect.length = 十;
rect.width = 五; // 矩形的长与阔关系较紧密亲密,放在一起。
char_poi = str;
½四-二:不用运用难懂的技巧性很高的话语,除非很有必要时。
说明:高技能话语不等于高效的程序,实质上程序的效率关键在于算法。
示范:如次表达式,考虑不周就可能出毛病,也较难了解。
* stat_poi ++ += 一;
* ++ stat_poi += 一;
应诀别改成如次。
*stat_poi += 一;
stat_poi++; // 此2话语效能无异于“ * stat_poi ++ += 一; ”
++ stat_poi;
*stat_poi += 一; // 此2话语效能无异于“ * ++ stat_poi += 一; ”
五 变量、构造
¹五-一:去掉没必要的公共变量。
说明:公共变量是增大模块间耦合的缘故之一,故应减小没必要的公共变量以减低模块间的耦合度。
¹五-二:认认真真定义并明确公共变量的含意、功用、取值范畴及公共变量间的关系。
说明:在对变量宣言的与此同时,应对其含意、功用及取值范畴开展诠释说明,与此同时若有必要还应说明毋宁它变量的关系。
¹五-三:明确公共变量与操作此公共变量的函数或历程的关系,如访问、批改及创办等。
说明:明确进程操作变量的关系后,将有益于程序的更进一步优化、单元测试、系统联调以及代码维护等。这种关系的说明可在诠释或文档中描述。
示范:在源文件中,可按如次诠释方式说明。
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,故而变量将唤起函数间较大的耦合,并也许增多代码测试、维护的难度。
¹五-四:应向公共变量传送数据时,要非常当心,防止赋与不合情理的值或越境等现象发作。
说明:对公共变量赋值时,若有必要应开展合法性稽查,以拔高代码的可靠性、稳定性。
¹五-五:防止局部变量与公共变量同名。
说明:若运用了较好的定名守则,那么此问题可自动化除。
¹五-六:严禁应用未经初始化的变量作为右值。
说明:特别是在C/C++中引述未经赋值的指针,常常会引起系统崩溃。
½五-一:结构仅有一个模块或函数可以批改、创设,而其他相关模块或函数只访问的公共变量,防止多个不同模块或函数都可以批改、创造同一公共变量的现象。
说明:减低公共变量耦合度。
½五-二:施用严厉模式定义的、可移栽的数据门类,尽可能不用施用与具体硬件或软件环境关系密切的变量。
说明:应用基准的数据部类,有益于程序的移栽。
示范:如次例证(在DOS停BC3.1环境中),在移栽时也许发生问题。
void main()
{
register int index; // 寄存器变量
_AX = 零x4000; // _AX是BC3.1提供的寄存器“伪变量”
... // program code
}
½五-三:构造的效能要单调,是针对一种事宜的抽象。
说明:设计构造时应力争使构造代表一种实际事宜的抽象,而不是同时代表多种。构造中的各元素应代表同一事宜的不同侧面,而不应把描述没有关系或关系很弱的不同仁务的元素放到同一构造中。
示范:如次构造不太明晰、合理。
typedef struct STUDENT_STRU
{
unsigned char name[八]; /* student's name */
unsigned char age; /* student's age */
unsigned char sex; /* student's sex, as follows */
/* 零 - FEMALE; 一 - MALE */
unsigned char
teacher_name[八]; /* the student teacher's name */
unisgned char
teacher_sex; /* his teacher sex */
} STUDENT;
若改成如次,或者更合理些。
typedef struct TEACHER_STRU
{
unsigned char name[八]; /* teacher name */
unisgned char sex; /* teacher sex, as follows */
/* 零 - FEMALE; 一 - MALE */
} TEACHER;
typedef struct STUDENT_STRU
{
unsigned char name[八]; /* student's name */
unsigned char age; /* student's age */
unsigned char sex; /* student's sex, as follows */
/* 零 - FEMALE; 一 - MALE */
unsigned int teacher_ind; /* his teacher index */
} STUDENT;
½五-四:不用设计面面皆到、十分灵便的数据结构。
说明:面面皆到、灵便的数据结构反是简单唤起误会和操作艰难。
½五-五:不同构造间的关系不用过于复杂。
说明:若两个构造间关系较复杂、紧密亲密,那么应合为一个构造。
示范:如次两个构造的结构不合情理。
typedef struct PERSON_ONE_STRU
{
unsigned char name[八];
unsigned char addr[40];
unsigned char sex;
unsigned char city[15];
} PERSON_ONE;
typedef struct PERSON_TWO_STRU
{
unsigned char name[八];
unsigned char age;
unsigned char tel;
} PERSON_TWO;
因为两个构造都是描述同一事物的,那么莫若合成一个构造。
typedef struct PERSON_STRU
{
unsigned char name[八];
unsigned char age;
unsigned char sex;
unsigned char addr[40];
unsigned char city[15];
unsigned char tel;
} PERSON;
½五-六:构造中元素的个数应适中。若构造中元素个数过剩可思考根据某种准则把元素结成不同的子构造,以减小原构造中元素的个数。
说明:增多构造的可理解性、操作性和可维护性。
示范:假若以为上述的_PERSON构造元素过剩,那么可如次对之区划。
typedef struct PERSON_BASE_INFO_STRU
{
unsigned char name[八];
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;
½五-七:仔仔细细设计构造中元素的格局与排列顺序,使构造简单了解、俭省占用空间,并减小唤起误用现象。
说明:合理排列构造中元素顺序,可俭省空间并增多可理解性。
示范:如次构造中的位域排列,将占较大空间,可读性也稍差。
typedef struct EXAMPLE_STRU
{
unsigned int valid: 一;
PERSON person;
unsigned int set_flg: 一;
} EXAMPLE;
若改为如次方式,不光可俭省1字节空间,可读性也变好了。
typedef struct EXAMPLE_STRU
{
unsigned int valid: 一;
unsigned int set_flg: 一;
PERSON person ;
} EXAMPLE;
½五-八:构造的设计要尽可能思考向前兼容和之后的版本升格,并为某些将来也许的运用保留余地(如预留一些空间等)。
说明:软件向前兼容的特点,是软件产品是不是顺利的主要标示之一。如若要想使产品具有较好的前向兼容,那么在产品设计之初就应为之后版本升格保存一定后路,并且在产品升级时务必思考前一版本的各种特点。
½五-九:留意具体语言及编译器处置不同数据门类的准则及相关细节。
说明:如在C语言中,static局部变量将在内存储器“数据区”中生成,而非static局部变量将在“堆栈”中生成。这些细节对程序品质的保证书非常重要。
½五-十:编程时,要注意数据门类的挟制变换。
说明:应进展数据门类挟制变换时,其数据的意思、变换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有也许留下隐患。
½五-11:对编译系统默许的数据门类变换,也要有充分的认识。
示范:如次赋值,半数以上编译器不发作告警,但值的含意仍是稍有变更。
char chr;
unsigned short int exam;
chr = -一;
exam = chr; // 编译器不发作告警,此时此刻exam为零xFFFF。
½五-12:尽可能减小没有必要的数据部类默许变换与挟制变换。
½五-13:合理地设计数据并应用自定义数据门类,避免数据间开展用不着的门类变换。
½五-14:对自定义数据部类开展恰当定名,使它变成自描述性的,以拔高代码可读性。注意其定名模式在同一产品中的一致。
说明:应用自定义门类,可以补救编程语言提供部类少、信息量不足的缺点,并能使程序明晰、简明。
示范:可参照如次模式宣言自定义数据部类。
下部的宣言可使数据门类的施用简明、了然。
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
底下的宣言可使数据部类具有更丰富的含意。
typedef float DISTANCE;
typedef float SCORE;
½五-15:应宣言用以分布式环境或不同CPU间通讯环境的数据结构时,务须思忖机器的字节顺序、施用的位域及字节对齐等问题 。
说明:例如Intel CPU与68360 CPU,在处置位域及整数时,其在内存储器寄存的“顺序”恰好相反。
示范:假如如同下短整数及构造。
unsigned short int exam;
typedef struct EXAM_BIT_STRU
{ /* Intel 68360 */
unsigned int A一: 一; /* bit 零 七 */
unsigned int A二: 一; /* bit 一 六 */
unsigned int A三: 一; /* bit 二 五 */
} EXAM_BIT;
如次是Intel CPU生成短整数及位域的模式。
内存储器: 零 一 二 ... (从低到高,以字节为机关)
exam exam低字节 exam高字节
内存储器: 零 bit 一 bit 二 bit ... (字节的各“位”)
EXAM_BIT A一 A二 A三
如次是68360 CPU生成短整数及位域的形式。
内存储器: 零 一 二 ... (从低到高,以字节为机构)
exam exam高字节 exam低字节
内存储器: 七 bit 六 bit 五 bit ... (字节的各“位”)
EXAM_BIT A一 A二 A三
说明:在对齐形式停,CPU的运作效率要快得多。
示范:如次图,应一个long型数(如图中long一)在内存储器中的位置恰好与内存储器的字地界对齐时,CPU存取这个数只需访问一次内存储器,而当一个long型数(如图中的long二)在内存储器中的位置超越了字边境时,CPU存取这个数就需要累次访问内存储器,如i960cx访问这么的数需读内存储器三次(一个BYTE、一个SHORT、一个BYTE,由CPU的微代码实施,对软件透明),全部对齐形式停CPU的运作效率显然快多了。
一 八 16 24 32
------- ------- ------- -------
| long一 | long一 | long一 | long一 |
------- ------- ------- -------
| | | | long二 |
------- ------- ------- --------
| long二 | long二 | long二 | |
------- ------- ------- --------
| ....
六 函数、进程
¹六-一:对所调用函数的差错回来码要仔仔细细、全面地处置。
¹六-二:明确函数效能,精确(而不是将近)地兑现函数设计。
¹六-三:编撰可重入函数时,当注意局部变量的施用(如编纂C/C++语言的可重入函数时,当应用auto即缺省态局部变量或寄存器变量)。
说明:编纂C/C++语言的可重入函数时,不应施用static局部变量,不然务必透过非一般处置,才力使函数具有可重入性。
¹六-四:编纂可重入函数时,若应用全局变量,则应透过关中止、信号量(即P、V操作)等手段对其加以保护。
说明:若对所应用的全局变量不加以保护,则此函数就不具有可重入性,即当多个历程调用此函数时,很有或者使相关全局变量变成不可知状态。
示范:假想Exam是int型全局变量,函数Squre_Exam回到Exam平方值。那么如次函数不具有可重入性。
unsigned int example( int para )
{
unsigned int temp;
Exam = para; // (**)
temp = Square_Exam( );
return temp;
}
此函数若被多个历程调用的话,其结果可能是未知的,由于应(**)话语刚施行完后,除此而外一个运用本函数的历程也许恰好被激活,那么当新激活的历程实施到此函数时,将使Exam赋与另一个不同的para值,之所以当统制从新返回“temp = Square_Exam( )”后,计算出的temp很可能不是料想中的结果。此函数应如次改善。
unsigned int example( int para )
{
unsigned int temp;
[申请信号量操作] // 若申请不到“信号量”,说明除此以外的过程正处于
Exam = para; // 给Exam赋值并计算其平方过程中(即在运用此
temp = Square_Exam( ); // 信号),本历程务必等候其开释信号后,才可继
[开释信号量操作] // 续实施。若申请到信号,则可持续实施,但其
// 它历程务须等候本过程开释信号量后,才识再使
// 用本信号。
return temp;
}
¹六-五:在同一项目组应明确规定接合口函数参数的合法性检察应由函数的调用者负责仍是由接口函数自身负责,缺省是由函数调用者负责。
说明:关于模块间接口函数的参数的合法性检察这一问题,往往有两个极端现象,即:要不是调用者和被调用者对参数均不作合法性稽查,结果就疏漏了合法性稽查这一必要的处置进程,促成问题隐患;要不乃是调用者和被调用者均对参数进展合法性检察,这种景况虽不会促成问题,但发生了冗余代码,减低了效率。
½六-一:防止将函数的参数作为工作变量。
说明:将函数的参数作为工作变量,有或者差错地改变参数内容,之所以很风险。对务须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
示范:停函数的兑现不太好。
void sum_data( unsigned int num, int *data, int *sum )
{
unsigned int count;
*sum = 零;
for (count = 零; count < num; count++)
{
*sum += data[count]; // sum成了工作变量,不太好。
}
}
若改成如次,则更好些。
void sum_data( unsigned int num, int *data, int *sum )
{
unsigned int count ;
int sum_temp;
sum_temp = 零;
for (count = 零; count < num; count ++)
{
sum_temp += data[count];
}
*sum = sum_temp;
}
½六-二:函数的规模尽可能限制在200行之内。
说明:不包括诠释和空格行。
½六-三:一个函数仅完成一件效能。
½六-四:为容易效能编纂函数。
说明:尽管为仅用一两行就可完成的效能去编函数好象没有必要,但用函数可使效能明确化,增多程序可读性,亦可便利维护、测试。
示范:如次话语的效能不很明显。
value = ( a > b ) ? a : b ;
改成如次就很明晰了。
int max (int a, int b)
{
return ((a > b) ? a : b);
}
value = max (a, b);
或改成如次。
#define MAX (a, b) (((a) > (b)) ? (a) : (b))
value = MAX (a, b);
½六-五:不用设计多用途面面皆到的函数。
说明:多功能集于一身的函数,很可能使函数的懂得、测试、维护等变得艰难。
½六-六:函数的效能应该是可以预测的,也乃是只要输入数据雷同就应发作一样的输出。
说明:含有内部“存储器”的函数的效能可能是不可预测的,由于它的输出也许取决内部存储器(如某标记)的状态。这么的函数既不便于懂得又有损于测试和维护。在C/C++语言中,函数的static局部变量是函数的内部存储器,有或者使函数的效能不可预测,然则,应某函数的回到值为指针门类时,则务须是STATIC的局部变量的地址作为回来值,若为AUTO种,则回到为错针。
示范:如次函数,其回到值(即效能)是不可预测的。
unsigned int integer_sum( unsigned int base )
{
unsigned int index;
static unsigned int sum = 零; // 注意,是static门类的。
// 若改成auto部类,则函数即成为可预测。
for (index = 一; index <= base; index++)
{
sum += index;
}
return sum;
}
½六-七:尽可能不用编撰依赖于其余函数内部兑现的函数。
说明:此条为函数独立性的基本要求。因为现阶段大多数高级语言都是构造化的,之所以经过具体语言的语法要求与编译器效能,根本就可以防止这种状况发作。但在编译语言中,因为其灵活性,很可能使函数出现这种景况。
示范:如次是在DOS停TASM的汇编程序例证。历程Print_Msg的兑现依赖于Input_Msg的具体兑现,这种程序是非曲直构造化的,难以维护、批改。
... // 程序代码
proc Print_Msg // 历程(函数)Print_Msg
... // 程序代码
jmp LABEL
... // 程序代码
endp
proc Input_Msg // 进程(函数)Input_Msg
... // 程序代码
LABEL:
... // 程序代码
endp
½六-八:避免设计多参数函数,不施用的参数从接口中去掉。
说明:目的减小函数间接口的复杂度。
½六-九:非部署函数应减小或防止统制参数,尽可能只施用数据参数。
说明:本提议目的是防止函数间的统制耦合。部署函数是指依据输入的讯息门类或统制下令,来起步呼应的效能实业(即函数或进程),而自身并不完成具体效能。统制参数是指改变函数效能举动的参数,即函数要根据此参数来决议具体怎么工作。非部署函数的统制参数增加了函数间的统制耦合,很可能使函数间的耦合度增大,并使函数的效能不独一。
示范:如次函数结构不太合理。
int add_sub( int a, int b, unsigned char add_sub_flg )
{
if (add_sub_flg == INTEGER_ADD)
{
return (a + b);
}
else
{
return (a b);
}
}
莫若分成如次两个函数明晰。
int add( int a, int b )
{
return (a + b);
}
int sub( int a, int b )
{
return (a b);
}
½六-十:检察函数全部参数输入的有效性。
½六-11:检察函数全部非参数输入的有效性,全数据文件、公共变量等。
说明:函数的输入重要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。函数在应用输入事前,当开展必要的检察。
½六-12:函数名应正确描述函数的效能。
½六-13:施用动宾短语为施行某操作的函数定名。如果是OOP步骤,可以唯有动词(名词是对象自身)。
示范:参考如次模式定名函数。
void print_record( unsigned int rec_ind ) ;
int input_record( void ) ;
unsigned char get_current_color( void ) ;
提议六-14:避免运用无意义或含意不清的动词为函数定名。
说明:避免用含意不清的动词如process、handle等为函数定名,由于这些动词并没有说明要具体做什么。
提议六-15:函数的回到值要明白、了然,让使用者不容易漠视错处状况。
说明:函数的每种失误回到值的意思要明晰、了然、正确,防止使用者误用、懂得错处或漠视差错回到码。
½六-16:除非必要,最好不用把与函数回到值部类不同的变量,以编译系统默许的变换模式或挟制的变换形式作为回来值回到。
½六-17:让函数在调用点显得易懂、简单懂得。
½六-18:在调用函数填空参数时,应尽可能减小没有必要的默许数据部类变换或挟制数据门类变换。
说明:由于数据门类变换或多或少存在风险。
½六-19:避免函数中用不着话语,防止程序中的渣滓代码。
说明:程序中的渣滓代码不光占用额外的空间,并且还经常影响程序的效能与性能,很可能给程序的测试、维护等促成用不着的烦劳。
½六-20:防止把没联系关系的话语放到一个函数中。
说明:防止函数或进程内出现随机内聚。随机内聚是指将没联系关系或联系关系很弱的话语放到同一个函数或过程中。随机内聚给函数或历程的维护、测试及之后的升格等造成了不方便,与此同时也使函数或进程的效能不明确。应用随机内聚函数,经常简单出现在一种施用处所亟需改善此函数,而另一种施用处所又不允许这种改善,故此陷入困境。
在编程时,常常碰到在不同函数中应用雷同的代码,好多开发人员都愿把这些代码提出来,并结成一个新函数。若这些代码联系关系较大并且是完成一个效能的,那么这种结构是合理的,不然这种结构将发生随机内聚的函数。
示范:如次函数乃是一种随机内聚。
void Init_Var( void )
{
Rect.length = 零;
Rect.width = 零; /* 初始化矩形的长与阔 */
Point.x = 十;
Point.y = 十; /* 初始化“点”的座标 */
}
矩形的长、阔与点的座标根本没任何关系,故之上函数是随机内聚。
当如次分成两个函数:
void Init_Rect( void )
{
Rect.length = 零;
Rect.width = 零; /* 初始化矩形的长与阔 */
}
void Init_Point( void )
{
Point.x = 十;
Point.y = 十; /* 初始化“点”的座标 */
}
½六-21:如其多段代码反复做同一件事儿,那么在函数的区划上或许存在问题。
说明:若此段代码各话语其间有实质性联系关系并且是完成同一件效能的,那么可思考把此段代码结构成一个新的函数。
½六-22:效能不明确较小的函数,特别是仅有一个上司函数调用它时,应试虑把它合拢到上司函数中,而不须独自存在。
说明:模块中函数区划的过剩,正常会使函数间的接口变得复杂。之所以过小的函数,特别是扇入很低的或效能不明确的函数,不值得独自存在。
½六-23:设计高扇入、合理扇出(小于七)的函数。
说明:扇出是指一个函数直接调用(统制)其它函数的数额,而扇入是指有几多上司函数调用它。
扇出过大,表明函数过头复杂,急需统制和协调过剩的下属函数;而扇出过小,如老是一,表明函数的调用层次或许过剩,这么有损程序翻阅和函数构造的分析,而且程序运行时会对系统资源如堆栈空间等促成压力。函数较合理的扇出(支配函数除此之外)通常是三-五。扇出太大,正常是因为匮缺中间层次,可适当增多中间层次的函数。扇出太小,可把部下函数更进一步分解多个函数,或合拢到上司函数中。当然分解或合龙函数时,不能改变要兑现的效能,也不能违反函数间的独立性。
扇入越大,表明应用此函数的上司函数越多,这么的函数施用效率高,但不能违反函数间的独立性而纯粹地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。
较比很好的软件构造通常是高层函数的扇出较高,中层函数的扇出较少,而底层函数则扇入到公共模块中。
½六-24:减小函数自身或函数间的递归调用。
说明:递归调用特别是函数间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用通常都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或效能的兑现便利,当减小没必要的递归调用。
½六-25:仔细分析模块的效能及性能需求,并更上一层楼细分,与此同时若有必要画出相关数据流图,据此来进展模块的函数区划与机构。
说明:函数的区划与单位是模块的兑现过程中很要害的方法,何以区划出合理的函数构造,关系到模块的终极效率和可维护性、可测性等。依据模块的效能图或/及数据流图投射出函数构造是惯用步骤之一。
½六-26:改善模块中函数的构造,减低函数间的耦合度,并拔高函数的独立性以及代码可读性、效率和可维护性。优化函数构造时,要信守以次准则:
(一)不能影响模块效能的兑现。
(二)认认真真视察模块或函数失误处置及模块的性能要求并进展完美。
(三)经过分解或归拢函数来改善软件构造。
(四)视察函数的规模,过大的要进展分解。
(五)减低函数间接口的复杂度。
(六)不同层次的函数调用要有较合理的扇入、扇出。
(七)函数效能应可预测。
(八)增高函数内聚。(单调效能的函数内聚最高)
说明:对初始区划后的函数构造应开展改善、优化,使之更是合理。
½六-27:在多任务操作系统的环境下编程,要注意函数可重入性的结构。
说明:可重入性是指函数可以被多个任务历程调用。在多任务操作系统中,函数是不是具有可重入性是是非非常重要的,由于这是多个过程可以共用此函数的先决条件。除此以外,编译器是不是提供可重入函数库,与它所服务的操作系统相干,惟独操作系统是多任务时,编译器才有或者提供可重入函数库。如DOS停BC和MSC等就不具备可重入函数库,由于DOS是单用户单任务操作系统。
½六-28:避免应用BOOL参数。
说明:缘故有2,其一是BOOL参数值无意义,TURE/FALSE的含意是非曲直常模模糊糊的,在调用时很难晓得该参数究竟传话的是什么意思;其二是BOOL参数值有损于扩张。再有NULL也是一个无意义的单纯词。
½六-29: 至于提供了回到值的函数,在引述时最好应用其回到值。
½六-30:应一个历程(函数)中对较长变量(通常是构造的成员)有较多摘引时,可以用一个意思对等的宏顶替。
说明:这么可以增多编程效率和程序的可读性。
示范:在某过程中较多引述TheReceiveBuffer[FirstSocket].byDataPtr,
则可以透过以次宏定义来顶替:
# define pSOCKDATA TheReceiveBuffer[FirstScoket].byDataPtr
七 可测性
¹七-一:在同一项目组或产品组内,要有一套一致的为集成测试与系统联调预备的调测开关及呼应打印函数,而且要有详细的说明。
说明:本守则是针对项目组或产品组的。
¹七-二:在同一项目组或产品组内,调测打印出的信息串的格式要有一致的模式。信息串中起码要有所在模块名(或源文件名)及行号。
说明:一致的调测信息格式易于集成测试。
¹七-三:编程的与此同时要为单元测试抉择恰当的测试点,并认真结构测试代码、测试工例,与此同时付出明确的诠释说明。测试代码一部分应作为(模块中的)一个头模块,以便利测试代码在模块中的装配与拆卸(经过调测开关)。
说明:为单元测试而预备。
¹七-四:在进展集成测试/系统联调事前,要结构好测试环境、测试项目及测试工例,与此同时仔细分析并优化测试工例,以增高测试效率。
说明:美的测试工例应尽量模拟出程序所碰到的边境值、各种复杂环境及一些极端景况等。
¹七-五:运用预言来发现软件问题,增高代码可测性。
说明:预言是对某种假想条件进行检查(可理解为若条件成立则无动作,不然应汇报),它可以高速发现并定位软件问题,与此同时对系统差错进展自动报警。预言可以对在系统中暗藏很深,用其它手段极难发现的问题进展定位,故而缩水软件问题定位时间,拔高系统的可测性。现实运用时,可根据具体情况灵便地设计预言。
示范:下边是C语言中的一个预言,用宏来设计的。(内中NULL为零L)
#ifdef _EXAM_ASSERT_TEST_ // 若施用预言测试
void exam_assert( char * file_name, unsigned int line_no )
{
printf( "/n[EXAM]Assert failed: %s, line %u/n",
file_name, line_no );
abort( );
}
#define EXAM_ASSERT( condition )
if (condition) // 若条件成立,则无动作
NULL;
else // 不然汇报
exam_assert( __FILE__, __LINE__ )
#else // 若不施用预言测试
#define EXAM_ASSERT(condition) NULL
#endif /* end of ASSERT */
¹七-六:用预言来检查程序一般运行时不应发作但在调测时有可能发生的非法状况。
¹七-七:不能用预言来检察终极产品肯定会出现且务必处置的错处景况。
说明:预言是用于处置不应该发作的差错景况的,关于可能会发作的且务必处置的景况要写防错程序,而不是预言。如某模块收到其它模块或链途中的讯息后,要对讯息的合理性进行检查,此进程为通常的差错检察,不能用预言来兑现。
¹七-八:对较复杂的预言加上明确的诠释。
说明:为复杂的预言加诠释,可澄清预言含意并减小用不着的误用。
¹七-九:用预言确认函数的参数。
示范:假定某函数参数中有一个指针,那么运用指针前可对它稽查,如次。
int exam_fun( unsigned char *str )
{
EXAM_ASSERT( str != NULL ); // 用预言检察“假想指针不为空”这个条件
... //other program code
}
¹七-十:用预言责任书没定义的特征或效能不被运用。
示范:假定某通讯模块在设计时,预备提供“无联接”和“联接” 这两种业务。但目前的版本中仅兑现了“无联接”业务,且在此版本的正式刊行版中,用户(上层模块)不应发作“联接”业务的请求,那么在测试时可用预言检察用户是不是运用“联接”业务。如次。
#define EXAM_CONNECTIONLESS 零 // 无连接业务
#define EXAM_CONNECTION 一 // 联接业务
int msg_process( EXAM_MESSAGE *msg )
{
unsigned char service; /* message service class */
EXAM_ASSERT( msg != NULL );
service = get_msg_service_class( msg );
EXAM_ASSERT( service != EXAM_CONNECTION ); // 假想不运用联接业务
... //other program code
}
¹七-11:用预言对程序开发环境(OS/Compiler/Hardware)的假定进行检查。
说明:程序运行时所需的软硬件环境及配备要求,不能用预言来稽查,而务必由一段专诚代码处置。用预言仅可对程序开发环境中的假想及所配备的某版本软硬件是不是具有某种效能的假定进行检查。如某网卡是不是在系统运作环境中配备了,应由程序中正式代码来稽查;而此网卡是不是具有某设想的效能,则可由预言来检察。
对编译器提供的效能及特点假想可用预言检察,原因是软件终极产品(即运作代码或机器码)与编译器已没任何直接关系,即软件运作过程中(注意不是编译过程中)不会也不应该对编译器的效能提担任何需求。
示范:用预言稽查编译器的int型数据占用的内存储器空间是不是为二,如次。
EXAM_ASSERT( sizeof( int ) == 二 );
¹七-12:正式软件产品中应把预言及其它调测代码去掉(即把相关的调测开关关掉)。
说明:加紧软件运作速度。
¹七-13:在软件系统中设立与撤除相干测试手段,不能对软件兑现的效能等产生影响。
说明:即有测试代码的软件和关掉测试代码的软件,在效能举动上应统一。
¹七-14:用调测开关来切换软件的DEBUG版和正式版,而不用与此同时存在正式版本和DEBUG版本的不同源文件,以减小维护的难度。
¹七-15:软件的DEBUG版本和刊行版本应当一致维护,不允许分家,而且要时时注意保证书两个版本在兑现功能上的一致性。
½七-一:在编纂代码预先,当先期设计好程序调试与测试的步骤和手段,并设计好各种调测开关及呼应测试代码如打印函数等。
说明:程序的调试与测试是软件生活周期中很主要的一个阶段,何以对软件进展较全面、高率的测试并尽量地找到软件中的错处就变为很要害的问题。因此在编撰源代码预先,除开要有一套比较完善的测试计划外,还应设计出一连串代码测试手段,为单元测试、集成测试及系统联调提供方便。
½七-二:调测开关应分成不平级别和门类。
说明:调测开关的设立及归类应从以次几方面思忖:针对模块或系统某一部分代码的调测;针对模块或系统某效能的调测;鉴于某种其它目的,如对性能、容量等的测试。这么做易于软件效能的调测,而且易于模块的单元测试、系统联调等。
½七-三:编纂防错程序,其后在处置差错以后可用预言宣布发作错处。
示范:假若某模块收到通讯链途中的讯息,则应对讯息的合法性进行检查,若讯息种类不是通信协议中规定的,则应进展疏失处置,以后可用预言汇报,如次例。
#ifdef _EXAM_ASSERT_TEST_ // 若运用预言测试
/* Notice: this function does not call 'abort' to exit program */
void assert_report( char * file_name, unsigned int line_no )
{
printf( "/n[EXAM]Error Report: %s, line %u/n",
file_name, line_no );
}
#define ASSERT_REPORT( condition )
if ( condition ) // 若条件成立,则无动作
NULL;
else // 要不然汇报
assert_report ( __FILE__, __LINE__ )
#else // 若不运用预言测试
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
int msg_handle( unsigned char msg_name, unsigned char * msg )
{
switch( msg_name )
{
case MSG_ONE:
... // 讯息MSG_ONE处置
return MSG_HANDLE_SUCCESS;
... // 其它合法讯息处置
default:
... // 讯息疏失处置
ASSERT_REPORT( FALSE ); // “合法”讯息不成立,汇报
return MSG_HANDLE_ERROR;
}
}
八 程序效率
¹八-一:编程时要常常注意代码的效率。
说明:代码效率分成大局效率、局部效率、时间效率及空间效率。大局效率是站在整个系统的视角上的系统效率;局部效率是站在模块或函数视角上的效率;时间效率是程序处置输入任务所需的时间长短;空间效率是程序所需内存储器空间,如机器代码空间大小、数据空间大小、栈空间大小等。
¹八-二:在保证书软件系统的正确性、稳定性、可读性及可测性的前提下,拔高代码效率。
说明:不能一味地追求代码效率,而对软件的正确性、稳定性、可读性及可测性促成影响。
¹八-三:局部效率应为大局效率服务,不能由于拔高局部效率而对大局效率促成影响。
¹八-四:透过对系统数据结构的区划与部门的改善,以及对程序算法的优化来拔高空间效率。
说明:这种形式是解决软件空间效率的基本办法。
示范:如次记要学生学习成绩的构造不合情理。
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef struct STUDENT_SCORE_STRU
BYTE name[八];
BYTE age;
BYTE sex;
BYTE class;
BYTE subject;
float score;
} STUDENT_SCORE;
由于每人学生都有多科学习成绩,故上述构造将占用较大空间。当如次改善(分成两个构造),总的贮存空间将变小,操作也变得更便利。
typedef struct STUDENT_STRU
{
BYTE name[八];
BYTE age;
BYTE sex;
BYTE class;
} STUDENT;
typedef struct STUDENT_SCORE_STRU
{
WORD student_index;
BYTE subject;
float score;
} STUDENT_SCORE;
¹八-五:循环体内工作量最小化。
说明:当认认真真思考循环体内的话语是不是可以放在循环体以外,使循环体内工作量最小,故而增高程序的时间效率。
示范:如次代码效率不高。
for (ind = 零; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
back_sum = sum; /* backup sum */
}
话语“back_sum = sum;”完全可以放在for话语以后,如次。
for (ind = 零; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
}
back_sum = sum; /* backup sum */
½八-一:仔细分析相关算法,并开展优化。
½八-二:仔仔细细视察、分析系统及模块处置输入(如事宜、讯息等)的形式,并加以改进。
½八-三:对模块中函数的区划及机关模式开展分析、优化,改善模块中函数的机构构造,增高程序效率。
说明:软件系统的效率重要与算法、处置任务形式、系统效能及函数构造有很大关系,仅在代码左右功夫正常不能解决根本问题。
½八-四:编程时,要时刻留意代码效率;优化代码时,要思忖周到。
½八-五:不应花过剩的时间玩儿命地增高调用不很频繁的函数代码效率。
说明:对代码优化可提高效率,但若考虑不周很有也许唤起严重后果。
½八-六:要仔仔细细地结构或直接用汇编编撰调用频繁或性能要求极高的函数。
说明:惟独对编译系统发作机器码的模式以及硬件系统较为熟习时,才可运用汇编嵌入形式。嵌入汇编可拔高时间及空间效率,但也存在一定危险。
½八-七:在保证书程序品质的前提下,经过压缩代码量、去掉用不着代码以及减小用不着的局部和全局变量,来拔高空间效率。
说明:这种形式对拔高空间效率可起到一定功用,但往往不能解决根本问题。
½八-八:在多重循环中,应将最忙的循环放在最内层。
说明:减小CPU切入循环层的次数。
示范:如次代码效率不高。
for (row = 零; row < 100; row++)
{
for (col = 零; col < 五; col++)
{
sum += a[row][col];
}
}
可以改成如次模式,以提高效率。
for (col = 零; col < 五; col++)
{
for (row = 零; row < 100; row++)
{
sum += a[row][col];
}
}
½八-九:尽可能减小循环嵌套层次。
½八-十:避免循环体内含判判案句,应将循环话语置于判断案句的代码块之中。
说明:目的是减小判断次数。循环体中的判断案句是不是可以移到循环体外,要瞧程序的具体情况而谈,正常景况,与循环变量无干的判断案句可以移到循环体外,而相干的则不可以。
示范:如次代码效率稍低。
for (ind = 零; ind < MAX_RECT_NUMBER; ind++)
{
if (data_type == RECT_AREA)
{
area_sum += rect_area[ind];
}
else
{
rect_length_sum += rect[ind].length;
rect_width_sum += rect[ind].width;
}
}
由于判判案句与循环变量无干,故可如次改善,以减小判断次数。
if (data_type == RECT_AREA)
{
for (ind = 零; ind < MAX_RECT_NUMBER; ind++)
{
area_sum += rect_area[ind];
}
}
else
{
for (ind = 零; ind < MAX_RECT_NUMBER; ind++)
{
rect_length_sum += rect[ind].length;
rect_width_sum += rect[ind].width;
}
}
½八-11:尽可能用乘法或其它步骤替代除法,特别是浮点演算中的除法。
说明:浮点演算除法要占用较多CPU资源。
示范:如次表达式演算可能要占较多CPU资源。
#define PAI 3.1416
radius = circle_length / (二 * PAI);
当如次把浮点除法改成浮点乘法。
#define PAI_RECIPROCAL (一 / 3.1416 ) // 编译器编译时,将生成具体浮点数
radius = circle_length * PAI_RECIPROCAL / 二;
½八-12:不用一味追求紧凑的代码。
说明:由于紧凑的代码并不代表高效率的机器码。
九 质量保证
¹九-一:在软件设计过程中构筑软件品质。
¹九-二:代码质量保证优先准则
(一)正确性,指程序要兑现设计要求的效能。
(二)稳定性、安全性,指程序稳固、靠得住、保险。
(三)可测试性,指程序要具有很好的可测试性。
(四)轨范/可读性,指程序书写作派、定名守则等要契合轨范。
(五)大局效率,指软件系统的通体效率。
(六)局部效率,指某个模块/子模块/函数的自身效率。
(七)个人表达方式/个人方便性,指个人编程习气。
¹九-三:只引述属于自个儿的贮存空间。
说明:若模块打包的较好,那么正常不会发作非法摘引旁人的空间。
¹九-四:防止摘引已经开释的内存储器空间。
说明:在现实编程过程中,稍不留心就会出现在一个模块中开释了某个内存储器块(如C语言指针),而另一模块在过后的某个时时又施用了它。要防止这种状况产生。
¹九-五:进程/函数中分配的内存储器,在进程/函数退出头里要开释。
¹九-六:历程/函数中申请的(为打开资料而运用的)资料句柄,在历程/函数退出预先要封闭。
说明:分配的内存储器不开释以及资料句柄不封锁,是较比常见的差错,并且稍不注意就有可能发生。这类差错往往会引起很严重后果,且难以定位。
示范:停函数在退出事前,没把分配的内存储器开释。
typedef unsigned char BYTE;
int example_fun( BYTE gt_len, BYTE *gt_code )
{
BYTE *gt_buf;
gt_buf = (BYTE *) malloc (MAX_GT_LENGTH);
... //program code, include check gt_buf if or not NULL.
/* global title length error */
if (gt_len > MAX_GT_LENGTH)
{
retur
本文来源:
我的异常网
Java Exception
Dotnet Exception
Oracle Exception