程序效率

〔八〕 =====[ 程序效率 ]===== 

?8-1 :编程时要经常注意代码的效率

说明:代码效率分为全局效率、局部效率、时间效率及空间效率。全局效率是站在整个系统的角度上的系统效率;局部效率是站在模块或函数角度上的效率;时间效率是程序处理输入任务所需的时间长短;空间效率是程序所需内存空间,如机器代码空间大小、数据空间大小、栈空间大小等。

 

?8-2 :在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效率

说明:不能一味地追求代码效率,而对软件的正确性、稳定性、可读性及可测性造成影响。

 

?8-3 :局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响

 

?8-4 :通过对系统数据结构的划分与组织的改进,以及对程序算法的优化来提高空间效率

说明:这种方式是解决软件空间效率的根本办法。

示例:如下记录学生学习成绩的结构不合理。

typedef unsigned char  BYTE;

typedef unsigned short WORD;

typedef struct STUDENT_SCORE_STRU

    BYTE name[8];

    BYTE age;

    BYTE sex;

    BYTE class;

    BYTE subject;

    float score;

} STUDENT_SCORE;

因为每位学生都有多科学习成绩,故如上结构将占用较大空间。应如下改进(分为两个结构),总的存贮空间将变小,操作也变得更方便。

typedef struct STUDENT_STRU

{

    BYTE name[8];

    BYTE age;

    BYTE sex;

    BYTE class;

} STUDENT;

 

typedef struct STUDENT_SCORE_STRU

{

    WORD student_index;

    BYTE subject;

    float score;

} STUDENT_SCORE;

 

?8-5 :循环体内工作量最小化

说明:应仔细考虑循环体内的语句是否可以放在循环体之外,使循环体内工作量最小,从而提高程序的时间效率。

示例:如下代码效率不高。

for (ind = 0; ind < MAX_ADD_NUMBER; ind++)

{

    sum += ind;

    back_sum = sum; /* backup sum */

}

语句“back_sum = sum;”完全可以放在for语句之后,如下。

for (ind = 0; ind < MAX_ADD_NUMBER; ind++)

{

    sum += ind;

}

back_sum  = sum; /* backup sum */

 

?8-1 :仔细分析有关算法,并进行优化

 

?8-2 :仔细考查、分析系统及模块处理输入(如事务、消息等)的方式,并加以改进

 

?8-3 :对模块中函数的划分及组织方式进行分析、优化,改进模块中函数的组织结构,提高程序效率

说明:软件系统的效率主要与算法、处理任务方式、系统功能及函数结构有很大关系,仅在代码上下功夫一般不能解决根本问题。

 

?8-4 :编程时,要随时留心代码效率;优化代码时,要考虑周全

 

?8-5 :不应花过多的时间拼命地提高调用不很频繁的函数代码效率

说明:对代码优化可提高效率,但若考虑不周很有可能引起严重后果。

 

?8-6 :要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数

说明:只有对编译系统产生机器码的方式以及硬件系统较为熟悉时,才可使用汇编嵌入方式。嵌入汇编可提高时间及空间效率,但也存在一定风险。

 

?8-7 :在保证程序质量的前提下,通过压缩代码量、去掉不必要代码以及减少不必要的局部和全局变量,来提高空间效率

说明:这种方式对提高空间效率可起到一定作用,但往往不能解决根本问题。

 

?8-8 :在多重循环中,应将最忙的循环放在最内层

说明:减少CPU切入循环层的次数。

示例:如下代码效率不高。

for (row = 0; row < 100; row++)

{

    for (col = 0; col < 5; col++)

    {

        sum += a[row][col];

    }

}

 

可以改为如下方式,以提高效率。

 

for (col = 0; col < 5; col++)

{

    for (row = 0; row < 100; row++)

    {

        sum += a[row][col];

    }

}

 

?8-9 :尽量减少循环嵌套层次

 

?8-10 :避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中

说明:目的是减少判断次数。循环体中的判断语句是否可以移到循环体外,要视程序的具体情况而言,一般情况,与循环变量无关的判断语句可以移到循环体外,而有关的则不可以。

示例:如下代码效率稍低。

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;

    }

}

因为判断语句与循环变量无关,故可如下改进,以减少判断次数。

 

if (data_type == RECT_AREA)

{

    for (ind = 0; ind < MAX_RECT_NUMBER; ind++)

    {

        area_sum += rect_area[ind];

    }

}

else

{

    for (ind = 0; ind < MAX_RECT_NUMBER; ind++)

    {

        rect_length_sum += rect[ind].length;

        rect_width_sum  += rect[ind].width;

    }

}

 

?8-11 :尽量用乘法或其它方法代替除法,特别是浮点运算中的除法

说明:浮点运算除法要占用较多CPU资源。

示例:如下表达式运算可能要占较多CPU资源。

 

#define PAI 3.1416

radius = circle_length / (2 * PAI);

应如下把浮点除法改为浮点乘法。

 

#define PAI_RECIPROCAL (1 / 3.1416 ) // 编译器编译时,将生成具体浮点数

radius = circle_length * PAI_RECIPROCAL / 2;

 

?8-12 :不要一味追求紧凑的代码

说明:因为紧凑的代码并不代表高效的机器码。

 

 

〔九〕 =====[ 质量保证 ]===== 

?9-1 :在软件设计过程中构筑软件质量

 

?9-2 :代码质量保证优先原则

     1)正确性,指程序要实现设计要求的功能。

     2)稳定性、安全性,指程序稳定、可靠、安全。

     3)可测试性,指程序要具有良好的可测试性。

     4)规范/可读性,指程序书写风格、命名规则等要符合规范。

     5)全局效率,指软件系统的整体效率。

     6)局部效率,指某个模块/子模块/函数的本身效率。

     7)个人表达方式/个人方便性,指个人编程习惯。

 

?9-3 :只引用属于自己的存贮空间

说明:若模块封装的较好,那么一般不会发生非法引用他人的空间。

 

?9-4 :防止引用已经释放的内存空间

说明:在实际编程过程中,稍不留心就会出现在一个模块中释放了某个内存块(如C语言指针),而另一模块在随后的某个时刻又使用了它。要防止这种情况发生。

 

?9-5 :过程函数中分配的内存,在过程函数退出之前要释放

 

?9-6 :过程函数中申请的(为打开文件而使用的)文件句柄,在过程函数退出之前要关闭

说明:分配的内存不释放以及文件句柄不关闭,是较常见的错误,而且稍不注意就有可能发生。这类错误往往会引起很严重后果,且难以定位。

示例:下函数在退出之前,没有把分配的内存释放。

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)

    {

        return GT_LENGTH_ERROR; // 忘了释放gt_buf

    }

    ...  // other program code

}

 

应改为如下。

 

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)

    {

        free( gt_buf  ); // 退出之前释放gt_buf

        return GT_LENGTH_ERROR; 

    }

    ...  // other program code

}

 

?9-7 :防止内存操作越界

说明:内存操作主要是指对数组、指针、内存地址等的操作。内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细小心。

示例:假设某软件系统最多可由10个用户同时使用,用户号为1-10,那么如下程序存在问题。

#define MAX_USR_NUM 10

unsigned char usr_login_flg[MAX_USR_NUM]= "";

void set_usr_login_flg( unsigned char usr_no )

{

    if (!usr_login_flg[usr_no])

    {

        usr_login_flg[usr_no]= TRUE;

    }

}

usr_no10时,将使用usr_login_flg越界。可采用如下方式解决。

void set_usr_login_flg( unsigned char usr_no )

{

    if (!usr_login_flg[usr_no - 1])

    {

        usr_login_flg[usr_no - 1]= TRUE;

    }

}

 

?9-8 :认真处理程序所能遇到的各种出错情况

 

?9-9 :系统运行之初,要初始化有关变量及运行环境,防止未经初始化的变量被引用

 

?9-10 :系统运行之初,要对加载到系统中的数据进行一致性检查

说明:使用不一致的数据,容易使系统进入混乱状态和不可知状态。

 

?9-11 :严禁随意更改其它模块或系统的有关设置和配置

说明:编程时,不能随心所欲地更改不属于自己模块的有关设置如常量、数组的大小等。

?9-12 :不能随意改变与其它模块的接口

 

?9-13 :充分了解系统的接口之后,再使用系统提供的功能

示例:在B型机的各模块与操作系统的接口函数中,有一个要由各模块负责编写的初始化过程,此过程在软件系统加载完成后,由操作系统发送的初始化消息来调度。因此就涉及到初始化消息的类型与消息发送的顺序问题,特别是消息顺序,若没搞清楚就开始编程,很容易引起严重后果。以下示例引自B型曾出现过的实际代码,其中使用了FID_FETCH_DATAFID_INITIAL初始化消息类型,注意B型机的系统是在FID_FETCH_DATA之前发送FID_INITIAL的。

MID alarm_module_list[MAX_ALARM_MID];

int FAR SYS_ALARM_proc( FID function_id, int handle )

{

    _UI i, j;

    switch ( function_id )

    {

        ... // program code

        case FID_INITAIL:

            for (i = 0; i < MAX_ALARM_MID; i++)

            {

                if (alarm_module_list[i]== BAM_MODULE // **

                   || (alarm_module_list[i]== LOCAL_MODULE)

                {

                    for (j = 0; j < ALARM_CLASS_SUM; j++)

                    {

                        FAR_MALLOC( ... );

                    }

                }

            }

            ... // program code

            break;

 

        case FID_FETCH_DATA:

            ... // program code

            Get_Alarm_Module( );  // 初始化alarm_module_list

            break;

 

        ... // program code

    }

}

由于FID_INITIAL是在FID_FETCH_DATA之前执行的,而初始化alarm_module_list是在FID_FETCH_DATA中进行的,故在FID_INITIAL中(**)处引用alarm_module_list变量时,它还没有被初始化。这是个严重错误。

 

应如下改正:要么把Get_Alarm_Module函数放在FID_INITIAL中(**)之前;要么就必须考虑(**)处的判断语句是否可以用(不使用alarm_module_list变量的)其它方式替代,或者是否可以取消此判断语句。

 

?9-14 :编程时,要防止差错误

说明:此类错误一般是由于把“<=”误写成“<”“>=”误写成“>”等造成的,由此引起的后果,很多情况下是很严重的,所以编程时,一定要在这些地方小心。当编完程序后,应对这些操作符进行彻底检查。

 

?9-15 :要时刻注意易混淆的操作符。当编完程序后,应从头至尾检查一遍这些操作符,以防止拼写错误

说明:形式相近的操作符最容易引起误用,如C/C++中的“=”“==”“|”“||”“&”“&&”等,若拼写错了,编译器不一定能够检查出来。

示例:如把“&”写成“&&”,或反之。

ret_flg = (pmsg->ret_flg & RETURN_MASK); 

被写为:

ret_flg = (pmsg->ret_flg && RETURN_MASK);

 

rpt_flg = (VALID_TASK_NO( taskno ) && DATA_NOT_ZERO( stat_data ));

被写为:

rpt_flg = (VALID_TASK_NO( taskno ) & DATA_NOT_ZERO( stat_data ));

?9-16 :有可能的话,if 语句尽量加上else 分支,对没有else 分支的语句要小心对待;switch 语句必须有default 分支

 

?9-17 Unix 下,多线程的中的子线程退出必需采用主动退出方式,即子线程应return 出口。

 

?9-18 :不要滥用goto 语句。

说明:goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。

 

?9-1 :不使用与硬件或操作系统关系很大的语句,而使用建议的标准语句,以提高软件的可移植性和可重用性

 

?9-2 :除非为了满足特殊需求,避免使用嵌入式汇编

说明:程序中嵌入式汇编,一般都对可移植性有较大的影响。

 

?9-3 :精心地构造、划分子模块,并按“ 接口” 部分及“ 内核” 部分合理地组织子模块,以提高“ 内核” 部分的可移植性和可重用性

说明:对不同产品中的某个功能相同的模块,若能做到其内核部分完全或基本一致,那么无论对产品的测试、维护,还是对以后产品的升级都会有很大帮助。

 

?9-4 :精心构造算法,并对其性能、效率进行测试

 

?9-5 :对较关键的算法最好使用其它算法来确认

 

?9-6 :时刻注意表达式是否会上溢、下溢

示例:如下程序将造成变量下溢。

unsigned char size ;

while (size-- >= 0) // 将出现下溢

{

    ... // program code

}

size等于0时,再减1不会小于0,而是0xFF,故程序是一个死循环。应如下修改。

char size; // unsigned char 改为char

while (size-- >= 0)

{

    ... // program code

}

 

?9-7 :使用变量时要注意其边界值的情况

示例:如C语言中字符型变量,有效值范围为-128127。故以下表达式的计算存在一定风险。

 

char chr = 127;

int sum = 200;

chr += 1; // 127chr的边界值,再加1将使chr上溢到-128,而不是128

sum += chr; // sum的结果不是328,而是72

 

chrsum为同一种类型,或表达式按如下方式书写,可能会好些。

 

sum = sum + chr + 1;

 

?9-8 :留心程序机器码大小(如指令空间大小、数据空间大小、堆栈空间大小等)是否超出系统有关限制

 

?9-9 :为用户提供良好的接口界面,使用户能较充分地了解系统内部运行状态及有关系统出错情况

 

?9-10 :系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救

 

?9-11 :对一些具有危险性的操作代码(如写硬盘、删数据等)要仔细考虑,防止对数据、硬件等的安全构成危害,以提高系统的安全性

 

?9-12 :使用第三方提供的软件开发工具包或控件时,要注意以下几点:

1)充分了解应用接口、使用环境及使用时注意事项。

2)不能过分相信其正确性。

3)除非必要,不要使用不熟悉的第三方工具包与控件。

说明:使用工具包与控件,可加快程序开发速度,节省时间,但使用之前一定对它有较充分的了解,同时第三方工具包与控件也有可能存在问题。

 

?9-13 :资源文件(多语言版本支持),如果资源是对语言敏感的,应让该资源与源代码文件脱离,具体方法有下面几种:使用单独的资源文件、DLL 文件或其它单独的描述文件(如数据库格式)

 

 

〔十〕 =====[ 代码编辑、编译、审查 ]===== 

?10-1 :打开编译器的所有告警开关对程序进行编译

 

?10-2 :在产品软件(项目组)中,要统一编译开关选项

 

?10-3 :通过代码走读及审查方式对代码进行检查

说明:代码走读主要是对程序的编程风格如注释、命名等以及编程时易出错的内容进行检查,可由开发人员自己或开发人员交叉的方式进行;代码审查主要是对程序实现的功能及程序的稳定性、安全性、可靠性等进行检查及评审,可通过自审、交叉审核或指定部门抽查等方式进行。

 

?10-4 :测试部测试产品之前,应对代码进行抽查及评审

 

?10-1 :编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失

 

?10-2 :同产品软件(项目组)内,最好使用相同的编辑器,并使用相同的设置选项

说明:同一项目组最好采用相同的智能语言编辑器,如Muiti EditorVisual Editor等,并设计、使用一套缩进宏及注释宏等,将缩进等问题交由编辑器处理。

 

?10-3 :要小心地使用编辑器提供的块拷贝功能编程

说明:当某段代码与另一段代码的处理功能相似时,许多开发人员都用编辑器提供的 块拷贝功能来完成这段代码的编写。由于程序功能相近,故所使用的变量、采用的表达式等在功能及命名上可能都很相近,所以使用块拷贝时要注意,除了修改相应 的程序外,一定要把使用的每个变量仔细查看一遍,以改成正确的。不应指望编译器能查出所有这种错误,比如当使用的是全局变量时,就有可能使某种错误隐藏下 来。

?10-4 :合理地设计软件系统目录,方便开发人员使用

说明:方便、合理的软件系统目录,可提高工作效率。目录构造的原则是方便有关源程序的存储、查询、编译、链接等工作,同时目录中还应具有工作目录----所有的编译、链接等工作应在此目录中进行,工具目录----有关文件编辑器、文件查找等工具可存放在此目录中。

 

?10-5 :某些语句经编译后产生告警,但如果你认为它是正确的,那么应通过某种手段去掉告警信息

说明:在Borland C/C++中,可用“#pragma  warn”来关掉或打开某些告警。

示例:

#pragma warn -rvl // 关闭告警

int examples_fun( void )

{

      // 程序,但无return语句。

}

#pragma warn +rvl // 打开告警

编译函数examples_fun时本应产生函数应有返回值告警,但由于关掉了此告警信息显示,所以编译时将不会产生此告警提示。

 

?10-6 :使用代码检查工具(如语言用PC-Lint )对源程序检查

 

?10-7 :使用软件工具(如 LogiSCOPE )进行代码审查

 

 

 

〔十一〕 =====[ 代码测试、维护 ]===== 

?11-1 :单元测试要求至少达到语句覆盖

?11-2 :单元测试开始要跟踪每一条语句,并观察数据流及变量的变化

?11-3 :清理、整理或优化后的代码要经过审查及测试

?11-4 :代码版本升级要经过严格测试

?11-5 :使用工具软件对代码版本进行维护

?11-6 :正式版本上软件的任何修改都应有详细的文档记录

?11-1 :发现错误立即修改,并且要记录下来

?11-2 :关键的代码在汇编级跟踪

?11-3 :仔细设计并分析测试用例,使测试用例覆盖尽可能多的情况,以提高测试用例的效率

?11-4 :尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试

?11-5 :仔细测试代码处理数据、变量的边界情况

?11-6 :保留测试信息,以便分析、总结经验及进行更充分的测试

?11-7 :不应通过“ ” 来解决问题,应寻找问题的根本原因

?11-8 :对自动消失的错误进行分析,搞清楚错误是如何消失的

?11-9 :修改错误不仅要治表,更要治本

?11-10 :测试时应设法使很少发生的事件经常发生

?11-11 :明确模块或函数处理哪些事件,并使它们经常发生

?11-12  坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发现问题

?11-13:去除代码运行的随机性(如去掉无用的数据、代码及尽可能防止并注意函数中的内部寄存器等),让函数运行的结果可预测,并使出现的错误可再现

 

 

 

 

〔十二〕 =====[  ]===== 

?12-1 :用宏定义表达式时,要使用完备的括号

示例:如下定义的宏都存在一定的风险。

#define RECTANGLE_AREA( a, b ) a * b

#define RECTANGLE_AREA( a, b ) (a * b)

#define RECTANGLE_AREA( a, b ) (a) * (b)

 

正确的定义应为:

 

#define RECTANGLE_AREA( a, b ) ((a) * (b))

 

?12-2: 将宏所定义的多条表达式放在大括号中

示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for语句的书写稍不符规范。

 

#define INTI_RECT_VALUE( a, b )

    a = 0;

    b = 0;

 

for (index = 0; index < RECT_TOTAL_NUM; index++)

    INTI_RECT_VALUE( rect.a, rect.b );

 

正确的用法应为:

 

#define INTI_RECT_VALUE( a, b )

{

    a = 0;

    b = 0;

}

 

for (index = 0; index < RECT_TOTAL_NUM; index++)

{

   INTI_RECT_VALUE( rect[index].a, rect[index].b );

}

 

?12-3: 使用宏时,不允许参数发生变化

示例:如下用法可能导致错误。

#define SQUARE( a ) ((a) * (a))

 

int a = 5;

int b;

b = SQUARE( a++ ); // 结果:a = 7,即执行了两次增1

 

正确的用法是:

 

b = SQUARE( a );

a++; // 结果:a = 6,即只执行了一次增1

 

===============================  End  ===================================

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值