华为软件编程规范和范例 6 —— 程序效率、质量保证

8.   程序效率


¹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.   质量保证


¹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_no为10时, 将使用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_DATA与FID_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 : 编程时, 要防止差1 错误
说明: 此类错误一般是由于把“<=”误写成“<”或“>=”误写成“>”等造成的, 由此引起的后果, 很多情况下是很严重的, 所以编程时, 一定要在这些地方小心。当编完程序后, 应对这些操作符进行彻底检查。


¹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语言中字符型变量, 有效值范围为-128到127。故以下表达式的计算存在一定风险。


char chr = 127;
int sum = 200;
chr += 1; // 127为chr的边界值, 再加1将使chr上溢到-128, 而不是128。
sum += chr; // 故sum的结果不是328, 而是72。


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


sum = sum + chr + 1;


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


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


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


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


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

(1)充分了解应用接口、使用环境及使用时注意事项。
(2)不能过分相信其正确性。
(3)除非必要, 不要使用不熟悉的第三方工具包与控件。
说明: 使用工具包与控件, 可加快程序开发速度, 节省时间, 但使用之前一定对它有较充分的了解, 同时第三方工具包与控件也有可能存在问题。


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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、清晰第一 清晰性是易于维护、易于重构的程序必需具备的特征。代码首先是给人读的,好的代码应当可以像文章一样发声朗诵出来。 目前软件维护期成本占整个生命周期成本的40%~90%。根据业界经验,维护期变更代码的成本,小型系统是开发期的5倍,大型系统(100万行代码以上)可以达到100倍。业界的调查指出,开发组平均大约一半的人力用于弥补过去的错误,而不是添加新的功能来帮助公司提高竞争力。 一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。 2、简洁为美 简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误。写的代码越多,意味着出错的地方越多,也就意味着代码的可靠性越低。因此,我们提倡大家通过编写简洁明了的代码来提升代码可靠性。 废弃的代码(没有被调用的函数和全局变量)要及时清除,重复代码应该尽可能提炼成函数。 3、选择合适的风格,与代码原有风格保持一致 产品所有人共同分享同一种风格所带来的好处,远远超出为了统一而付出的代价。在公司已有编码规范的指导下,审慎地编排代码以使代码尽可能清晰,是一项非常重要的技能。 如果重构/ / 修改其他风格的代码时,比较明智的做法是根据 现有 代码 的 现有风格继续编写代码,或者使用格式转换工具进行转换成公司内部风格。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值