C语言编程培训教程资料

发布于:2021-11-28 12:47:30

C语言 编程培训

目录
1. 排版 2. 注释 3. 标识符命名 4. 可读性 5. 变量、结构 6. 函数、过程 7. 可测性 8. 程序效率 9. 质量保证 10. 代码编辑、编译、审查 11. 代码测试、维护

1.排版
? 程序块要采用缩进风格编写,缩进的空格数为4个。 说明:对于由开发工具自动生成的代码可以有不一致。
? 相对独立的程序块之间、变量说明之后必须加空行。 if (!a) { ... // code }

Val_1 = Val_2; Val_3 = Val_4;

加入空格

? 较长的语句( >80字符或超过编辑器可视范围)要分成多行书写, 长表达式 要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进 行适当的缩进,使排版整齐,语句可读。

perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN + STAT_SIZE_PER_FRAM * sizeof( _UL );

1.排版
? 循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表 达式要在低优先级操作符处划分新行,操作符放在新行之首。 if ((Val_aaaaaaaaa < Val_bbbbbbbbb) && (Function_aaaaaaaa(Val_cccccccc))) { ... // program code }
? 若函数或过程中的参数较长,则要进行适当的划分。
Function_aaaaaaaa ( (char *) & Val_aaaaaaaaa, (char *) & Val_bbbbbbbbb, sizeof(Val_ccccccccccccc));
? 不允许把多个短语句写在一行中,即一行只写一条语句。 Val_a = 1; Val_b = 2;
? if、 for、do、 while等语句的执行语句部分无论多少都要加括号{}。 if ((Val_aaaaaaaaa < Val_bbbbbbbbb)) { return; }

1.排版
? 程序块的分界符(如C/C++语言的大括号‘ {’和‘ }’)应各独占一行并且位于 同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构 的定义、枚举的定义以及if、 for、 do、 while、 switch、 case语句中的程序 都要采用如上的缩进方式。
for (...) { ... // program code
} ? 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、
之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符 (如->)和一元操作符后不应加空格。
int Val_a, Val_b, Val_c;
Val_a = 1;
Val_b = Val_a + 2;
Val_c = !Val_b;
p->Id = Pid ? if、 for、 while、 switch 等与后面的括号间应加空格,使 if 等关键字更为突出、
明显。
if (Val_a == Val_b)

1.排版

? 结构型的数组、多维的数组如果在定义时初始化,按照数组的矩阵结构分行 书写。

int aiNumbers[4][3] =

{

1, 1, 1,

2, 4, 8,

3, 9, 27,

4, 16, 64

}

? 相关的赋值语句等号对齐。

tPDBRes.wHead

= 0;

tPDBRes.wTail

= wMaxNumOfPDB - 1;

tPDBRes.wFree

= wMaxNumOfPDB;

tPDBRes.wAddress = wPDBAddr;

tPDBRes.wSize

= wPDBSize;

2.注释
? 一般情况下,源程序有效注释量必须在20%以上。
? 源文件及说明性文件(如头文件.h文件、 .inc文件、 .def文件、编译说明文 件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、 作者、内容/模块/主要函数、功能、与其它文件的关系、修改日志等,头文件 的注释中还应有函数功能简要说明。
/************************************************* Copyright: //版权说明 File name: // 文件名 Author: Version: Date: // 作者、版本及完成日期 Description: // 用于详细说明此程序(模块)的主要功能,与其他模块或函数的接口,输出值、取值范围、 含义及参数间的控制、顺序、独立或依赖等关系 Others: // 其它内容的说明 Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明
1. .... History: // 修改历史记录列表
1. Date: //修改日期 Author: //修改者 Modification: //修改内容简述 2. ...
*************************************************/

2.注释

? 程序中的定义,申明和实现,应该按照类别分开,并按如下顺序注释。

/***************************************************************************

* Types

类型定义

***************************************************************************/

/***************************************************************************

* Manifest Constants

常量

***************************************************************************/

/***************************************************************************

* Variables

全局变量

***************************************************************************/

/***************************************************************************

* Function prototypes

函数原型

***************************************************************************/

? 注释符与注释内容之间要用一个空格进行分隔。

/* 注释内容 */

2.注释
? 函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回 值、调用关系(函数、表)等。
/************************************************* Function: // 函数名称 Description: // 函数功能、性能等的描述 Calls: // 被本函数调用的函数清单 Called By: // 调用本函数的函数清单 Input: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系。 Output: // 对输出参数的说明。 Return: // 函数返回值的说明 Others: // 其它说明 *************************************************/
? 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。 不再有用的注释要删除。
? 注释的内容要清楚、明了,含义准确,防止注释二义性。 ? 注释应与其描述的代码相*,对代码的注释应放在其上方或右方(对单条语句
的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔 开
/* notes on Val_a */
Val_a = 1;

2.注释
? 对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明 时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方 相邻位置或右方。 /* notes on Def_a */ #define Def_a 100 /* notes on Def_a */
? 数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的, 必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对 结构中的每个域的注释放在此域的右方。
/* notes */
typedef struct {
char Val_a /* notes on Val_a */ } Struct_a;

2.注释
? 全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存 取它以及存取时注意事项等的说明。
/* function */ // 变量作用、含义 /* 0 - xxx, 1 - xxx*/ //变量取值范围 /* Call by Function */ //那些函数或过程调用它 /* Attention */ //注意事项 /* How to use Val_a */ // 使用方法 char Val_a;
? 注释与所描述内容进行同样的缩排。
/* How to use Val_a */ // 使用方法
char Val_a;
? 将注释与其上面的代码用空行隔开。 ? 对变量的定义和分支语句(条件分支、循环语句等)必须编写注释。
char Val_a; /* notes on Val_a */ if (...) {
Fuc_a(); /* notes on Fuc_a */ }

2.注释
? 对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进 入下一个case处理,必须在该case语句处理完、下一个case语句前加上明确 的注释。
case 0: if (...) {
break; } else {
Fuc_a(); /* now jump to case 1 */ } case 1:
Val_a = Val_b + 1; break;
? 避免在一行代码或表达式的中间插入注释。 ? 在代码的功能、意图层次上进行注释,以提供有用、额外的信息,无需做意义
不大的注释

2.注释
? 当代码段较长,特别是多重嵌套时,在程序块的结束行右方加注释标记,以 表明某程序块的结束。
if (...) {
... ... } /* end of if() */ //指明是哪个if语句的结束
? 注释格式尽量统一,建议使用“/* …… */ ” ? 注释应考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,建议
多使用中文,除非能用非常流利准确的英文表达。

3.标识符命名

? 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本 可以理解的缩写,避免使人产生误解。较短的单词可通过去掉“元音”形成 缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的 缩写。
temp 可缩写为 tmp ; flag 可缩写为 flg ; increment 可缩写为 inc ; message 可缩写为 msg ;
? 命名中若使用特殊约定或缩写,则要有注释说明。
? 对于变量命名,禁止取单个字符(如i、 j、 k...),建议除了要有具体含义外, 还能表明其变量类型、数据类型等,但i、 j、 k作局部循环变量是允许的。

int liv_hight;

其变量名解释如下: l i v hight

局部变量(Local) 整型(interger) 变量(variable) 变量含义

? 命名规范必须与所使用的系统风格保持一致,并在同一项目中统一,即大小 写,下划线之类的统一。

? 除非必要,不要用数字或较奇怪的字符来定义标识符。

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

? 除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下 划线开始和结尾的定义

4 可读性

? 注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先

级。

下列语句中的表达式

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)造成了判断条件出错。

? 避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有

物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。下

面的语句中如果1和0代表状态的话,则用有意义的枚举或宏来代替。

if (Val_a == 1)

{

Val_b = 0;

}

4 可读性
? 源程序中关系较为紧密的代码应尽可能相邻。 Val_a[0]=1; Val_b=2; Val_a[1]=3;
? 不要使用难懂的技巧性很高的语句,除非很有必要时。 Val_a ++ += 1; 应修改为 Val_a += 1; Val_a ++;
? 一条语句只完成一个功能。
Val_a= Val_b + GetValue(&Val_b); // 一条语句实现多个功能, Val_b有两种用途。

5 变量、结构

? 去掉没必要的公共变量,公共变量是增大模块间耦合的原因之一,故应减少 没必要的公共变量以降低模块间的耦合度。
? 仔细定义并明确公共变量的含义、作用、取值范围及公共变量间的关系。在 对变量声明的同时,应对其含义、作用及取值范围进行注释说明,同时若有 必要还应说明与其它变量的关系。
? 明确公共变量与操作此公共变量的函数或过程的关系,如访问、修改及创建 等。

Student Score

System_Init
创建 创建

Input_Rec
修改 修改

Print_Rec
访问 访问

Stat_Score
修改 访问,修改

? 当向公共变量传递数据时,要十分小心,防止赋与不合理的值或越界等现象发 生。

? 防止局部变量与公共变量同名。

? 严禁使用未经初始化的变量作为右值。特别是在 C/C++中引用未经赋值的指 针,经常会引起系统崩溃。

? 构造仅有一个模块或函数可以修改、创建,而其余有关模块或函数只访问的公 共变量,防止多个不同模块或函数都可以修改、创建同一公共变量的现象。

5 变量、结构

? 结构的功能要单一,是针对一种事务的抽象。

typedef struct STUDENT_STRU {
unsigned char name[8]; unsigned char age; unsigned char teacher_name[8]; unisgned char teacher_age; } STUDENT;

/* student‘s name */ /* student’s age */ /* teacher‘s name */ /* teacher’s age */

若改为如下,可能更合理些。

typedef struct STUDENT_STRU {
unsigned char name[8]; unsigned char age; } STUDENT;

/* student‘s name */ /* student’s age */

typedef struct TEACHER_STRU {
unsigned char teacher_name[8]; unisgned char teacher_age; } TEACHER;

/* teacher‘s name */ /* teacher’s age */

? 不要设计面面俱到、非常灵活的数据结构。

? 不同结构间的关系不要过于复杂。两个结构都是描述同一事物的,那么不如合 成一个结构。

5 变量、结构
? 结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应 用保留余地。软件向前兼容的特性,是软件产品是否成功的重要标志之一。 如果要想使产品具有较好的前向兼容,那么在产品设计之初就应为以后版本 升级保留一定余地,并且在产品升级时必须考虑前一版本的各种特性。
? 留心具体语言及编译器处理不同数据类型的原则及有关细节。如在 C 语言中, static 局部变量将在内存“数据区”中生成,而非 static 局部变量将在“堆栈” 中生成。这些细节对程序质量的保证非常重要。
? 编程时,要注意数据类型的强制转换。
? 对编译系统默认的数据类型转换,也要有充分的认识。如下赋值,多数编译 器(IAR)不产生告警,但值的含义还是稍有变化
char chr; unsigned short int exam; chr = -1; exam = chr; // 编译器不产生告警,此时 exam 为 255(0xFF)。
? 尽量减少没有必要的数据类型默认转换与强制转换。
? 合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转换。
? 对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。 注意其命名方式在同一产品中的统一,如IAR中 u8 定义为 unsigned short int。

5 变量、结构
? 不可将布尔变量和逻辑表达式直接与TRUE、FALSE或者1、0进行比较。
if (bFlag == TRUE)
? 在条件判断语句中,当整型变量与0 比较时,不可模仿布尔变量的风格,应当 将整型变量用“==”或“!=”直接与0比较。
if (Val_a == 0)
if (Val_a )
? 不可将浮点变量用“==”或“!=”与任何数字比较。 if (Val_a == 0.0)
? 应当将指针变量用“==”或“!=”与NULL比较。
if (pHead == NULL)
? 不可在for 循环体内修改循环变量,防止for 循环失去控制。且建议循环嵌套次 数不大于3次。
? 如果函数没有参数,则用void填充。 ? 当结构变量作为参数时,应传送结构的指针而不传送整个结构体,并且不得
修改结构中的元素,用作输出时除外。 ? 避免使用BOOLEAN参数。

6 函数、过程
? 对所调用函数的错误返回码要仔细、全面地处理。 ? 明确函数功能,精确(而不是*似)地实现函数设计。 ? 在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负
责还是由接。 ? 防止将函数的参数作为工作变量。
void sum_data(unsigned int sum) {
unsigned int count; ... sum = count; // sum 成了工作变量,不太好。 ... }
建议改为 void sum_data(unsigned int num) {
unsigned int count; unsigned int sum_temp; ... sum_temp = count; // sum 成了工作变量,不太好。 ... }

6 函数、过程
? 函数的规模尽量限制在200行以内。
? 一个函数仅完成一件功能。
? 为简单功能编写函数。
? 不要设计多用途面面俱到的函数。
? 非调度函数应减少或防止控制参数,尽量只使用数据参数,以防止函数间的 控制耦合。
如下函数构造不太合理。
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); } } 不如分为2个

6 函数、过程
? 检查函数所有参数输入的有效性。
? 检查函数所有非参数输入的有效性,如数据文件、公共变量等。
? 函数名应准确描述函数的功能。
? 避免用含义不清的动词如 process、 handle 等为函数命名,因为这些动词并 没有说明要具体做什么。
? 使用动宾词组为执行某操作的函数命名。如果是OOP方法,可以只有动词 (名词是对象本身)。
void print_record( unsigned int rec_ind ) ; unsigned char get_current_color( void ) ;
? 避免用含义不清的动词如 process、 handle 等为函数命名,因为这些动词并 没有说明要具体做什么。
? 函数的返回值要清楚、明了,让使用者不容易忽视错误情况。
? 除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转 换方式或强制的转换方式作为返回值返回。
? 让函数在调用点显得易懂、容易理解。
? 在调用函数填写参数时,应尽量减少没有必要的默认数据类型转换或强制数 据类型转换

6 函数、过程
? 避免函数中不必要语句,防止程序中的垃圾代码。程序中的垃圾代码不仅占 用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的测试、 维护等造成不必要的麻烦。
? 防止把没有关联的语句放到一个函数中。防止函数或过程内出现随机内聚。 随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程中。随机 内聚给函数或过程的维护、测试及以后的升级等造成了不便,同时也使函数 或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需 要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境。如下 函数就是一种随机内聚:
void Init_Var( void ) {
Rect.length = 0; Rect.width = 0; /* 初始化矩形的长与宽 */
Point.x = 10; Point.y = 10; /* 初始化“点”的坐标 */ } 不如分开来写为2个函数
? 如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题
? 功能不明确较小的函数,特别是仅有一个上级函数调用它时,应考虑把它合 并到上级函数中,而不必单独存在。

6 函数、过程
? 设计高扇入、合理扇出(小于7)的函数。扇出是指一个函数直接调用(控制) 其它函数的数目,而扇入是指有多少上级函数调用它。扇出过大,表明函数 过分复杂,而扇出过小,如总是 1,表明函数的调用层次可能过多,这样不利 程序阅读和函数结构的分析,并且程序运行时会对系统资源如堆栈空间等造 成压力。函数较合理的扇出(调度函数除外)通常是 3-5。扇出太大,一般是 由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数 进一步分解多个函数,或合并到上级函数中。当然分解或合并函数时,不能 改变要实现的功能,也不能违背函数间的独立性。扇入越大,表明使用此函 数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而 单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。较良 好的软件结构通常是顶层函数的扇出较高,中层函数的扇出较少,而底层函 数则扇入到公共模块中。
? 减少函数本身或函数间的递归调用,递归调用特别是函数间的递归调用(如 A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源 (如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功 能的实现方便,应减少没必要的递归调用。

6 函数、过程
? 避免使用BOOL参数。 ? 对于提供了返回值的函数,在引用时最好使用其返回值。 ? 改进模块中函数的结构,降低函数间的耦合度,并提高函数的独立性以及代
码可读性、效率和可维护性。优化函数结构时,要遵守以下原则:
( 1)不能影响模块功能的实现。 ( 2)仔细考查模块或函数出错处理及模块的性能要求并进行完善。 ( 3)通过分解或合并函数来改进软件结构。 ( 4)考查函数的规模,过大的要进行分解。 ( 5)降低函数间接口的复杂度。 ( 6)不同层次的函数调用要有较合理的扇入、扇出。 ( 7)函数功能应可预测。 ( 8)提高函数内聚。(单一功能的函数内聚最高)

7 可测性
? 在同一项目组或产品组内,要有一套统一的为集成测试与系统联调准备的调 测开关及相应打印函数,并且要有详细的说明。
? 在同一项目组或产品组内,调测打印出的信息串的格式要有统一的形式。信 息串中至少要有所在模块名(或源文件名)及行号。
? 编程的同时要为单元测试选择恰当的测试点,并仔细构造测试代码、测试用 例,同时给出明确的注释说明。测试代码部分应作为(模块中的)一个子模 块,以方便测试代码在模块中的安装与拆卸(通过调测开关)。
? 在进行集成测试/系统联调之前,要构造好测试环境、测试项目及测试用例, 同时仔细分析并优化测试用例,以提高测试效率。好的测试用例应尽可能模 拟出程序所遇到的边界值、各种复杂环境及一些极端情况等。
? 使用断言来发现软件问题,提高代码可测性。断言是对某种假设条件进行检 查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位 软件问题,同时对系统错误进行自动报警。断言可以对在系统中隐藏很深, 用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系 统的可测性。实际应用时,可根据具体情况灵活地设计断言。断言是用来处 理不应该发生的错误情况的,对于可能会发生的且必须处理的情况要写防错 程序,而不是断言。如某模块收到其它模块或链路上的消息后,要对消息的 合理性进行检查,此过程为正常的错误检查,不能用断言来实现。
? 用断言确认函数的参数。

7 可测性
? 在同一项目组或产品组内,要有一套统一的为集成测试与系统联调准备的调 测开关及相应打印函数,并且要有详细的说明。
? 正式软件产品中应把断言及其它调测代码去掉(即把有关的调测开关关掉)。 加快程序运行速度。
? 在软件系统中设置与取消有关测试手段,不能对软件实现的功能等产生影响。
? 用调测开关来切换软件的DEBUG版和正式版,而不要同时存在正式版本和 DEBUG版本的不同源文件,以减少维护的难度。
? 软件的DEBUG版本和发行版本应该统一维护,不允许分家,并且要时刻注意 保证两个版本在实现功能上的一致性。
? 编写防错程序,然后在处理错误之后可用断言宣布发生错误。

8 程序效率
? 代码效率分为全局效率、局部效率、时间效率及空间效率。全局效率是站在 整个系统的角度上的系统效率;局部效率是站在模块或函数角度上的效率; 时间效率是程序处理输入任务所需的时间长短;空间效率是程序所需内存空 间,如机器代码空间大小、数据空间大小、栈空间大小等。
? 代码效率分为全局效率、局部效率、时间效率及空间效率。全局效率是站在 整个系统的角度上的系统效率;局部效率是站在模块或函数角度上的效率; 时间效率是程序处理输入任务所需的时间长短;空间效率是程序所需内存空 间,如机器代码空间大小、数据空间大小、栈空间大小等。
? 在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效 率。
? 局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响。
? 通过对系统数据结构的划分与组织的改进,以及对程序算法的优化来提高空 间效率。
? 循环体内工作量最小化。
? 仔细分析有关算法,并进行优化。
? 不应花过多的时间拼命地提高调用不很频繁的函数代码效率。
? 要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数。

8 程序效率
? 在保证程序质量的前提下,通过压缩代码量、去掉不必要代码以及减少不必 要的局部和全局变量,来提高空间效率。
? 在多重循环中,应将最忙的循环放在最内层。
? 尽量减少循环嵌套层次。
? 避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中。
如下代码效率稍低: for (ind = 0; 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; } }
? 尽量用乘法或其它方法代替除法,特别是浮点运算中的除法。浮点运算除法 要占用较多 CPU 资源。
? 不要一味追求紧凑的代码。

9 质量保证
? 代码质量保证优先原则 ( 1)正确性,指程序要实现设计要求的功能。 ( 2)稳定性、安全性,指程序稳定、可靠、安全。 ( 3)可测试性,指程序要具有良好的可测试性。 ( 4)规范/可读性,指程序书写风格、命名规则等要符合规范。 ( 5)全局效率,指软件系统的整体效率。 ( 6)局部效率,指某个模块/子模块/函数的本身效率。 ( 7)个人表达方式/个人方便性,指个人编程*惯。
? 只引用属于自己的存贮空间。若模块封装的较好,那么一般不会发生非法引 用他人的空间。
? 防止引用已经释放的内存空间,入指针。
? 防止内存操作越界。内存操作主要是指对数组、指针、内存地址等的操作。 内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进 行这些操作时一定要仔细小心。
? 系统运行之初,要初始化有关变量及运行环境,防止未经初始化的变量被引 用。
? 统运行之初,要对加载到系统中的数据进行一致性检查。
? 编程时,不能随心所欲地更改不属于自己模块的有关设置如常量、数组的大 小等。

9 质量保证
? 不能随意改变与其它模块的接口。 ? 充分了解系统的接口之后,再使用系统提供的功能。 ? 编程时,要防止差1错误。 ? 要时刻注意易混淆的操作符。当编完程序后,应从头至尾检查一遍这些操作
符,以防止拼写错误。如 C/C++中的“ =”与“ ==”、“ |”与“ ||”、“ &”与 “ &&”等。 ? 有可能的话, if语句尽量加上else分支,对没有else分支的语句要小心对待; switch语句必须有default分支。 ? 不要滥用goto语句。goto 语句会破坏程序的结构性,所以除非确实需要,最 好不使用 goto 语句。 ? 不使用与硬件或操作系统关系很大的语句。 ? 除非为了满足特殊需求,避免使用嵌入式汇编。 ? 对较关键的算法最好使用其它算法来确认。 ? 时刻注意表达式是否会上溢、下溢。 ? 使用变量时要注意其边界值的情况。 ? 留心程序机器码大小(如指令空间大小、数据空间大小、堆栈空间大小等) 是否超出系统有关限制。

9 质量保证
? 系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自 动补救。
? 对一些具有危险性的操作代码(如写、删数据等)要仔细考虑,防止对数据、 硬件等的安全构成危害,以提高系统的安全性。
? 使用第三方提供的软件开发工具包或控件时,要注意以下几点: ( 1)充分了解应用接口、使用环境及使用时注意事项。 ( 2)不能过分相信其正确性。 ( 3)除非必要,不要使用不熟悉的第三方工具包与控件。
? 资源文件(多语言版本支持),如果资源是对语言敏感的,应让该资源与源 代码文件脱离,具体方法有下面几种:使用单独的资源文件、 DLL文件或其 它单独的描述文件(如数据库格式)。
?

10 代码编辑、编译、审查
? 打开编译器的所有告警开关对程序进行编译。 ? 通过自审、交叉审核或指定部门抽查等方式对代码进行审查,代码审查主要
是对程序实现的功能及程序的稳定性、安全性、可靠性等进行检查及评审。 ? 测试部测试产品之前,应对代码进行抽查及评审。 ? 编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因
造成代码丢失。 ? 同产品软件(项目组)内,最好使用相同的编辑器,并使用相同的设置选项。
我们公司使用IAR 6.0版本的编译软件。 ? 合理地设计软件系统目录,方便开发人员使用。 ? 某些语句经编译后产生告警,但如果你认为它是正确的,那么应通过某种手
段去掉告警信息。 ? 使用代码检查工具(如C语言用PC-Lint)对源程序检查。 ? 使用软件工具(如 LogiSCOPE)进行代码审查。

11 代码测试、维护
? 单元测试要求至少达到语句覆盖。 ? 单元测试开始要跟踪每一条语句,并观察数据流及变量的变化。 ? 清理、整理或优化后的代码要经过审查及测试。 ? 代码版本升级要经过严格测试。 ? 使用工具软件对代码版本进行维护。 ? 正式版本上软件的任何修改都应有详细的文档记录。 ? 发现错误立即修改,并且要记录下来。 ? 尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试。 ? 仔细测试代码处理数据、变量的边界情况。 ? 保留测试信息,以便分析、总结经验及进行更充分的测试。 ? 对自动消失的错误进行分析,搞清楚错误是如何消失的。 ? 测试时应设法使很少发生的事件经常发生。 ? 明确模块或函数处理哪些事件,并使它们经常发生。 ? 坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发
现问题。 ? 去除代码运行的随机性(去掉无用的数据、代码及尽可能防止并注意函数中
的“内部寄存器”等),让函数运行的结果可预测,并使出现的错误可再现。


相关推荐

最新更新

猜你喜欢