(最新)C语言编码
规范
编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载
(1)
HX/QI/012001
编号:HX/
C语言编码规范
项目编号:
部 门: 安全产品部
拟 制: 2009年3月28日
审 核: 年月日
批 准: 年月日
发放编号:
非受控 受控状态:
武汉虹旭信息技术有限责任公司
第 0 页 共 58 页
HX/QI/012001
修改
记录
混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载
修改号 修改/生效日期 修 改 页 次 修 改 理 由 0 3.28.2009 文档新建
0 3.28.2009
修改号 编 写 审 核 批 准 批准日期
第 1 页 共 58 页
HX/QI/012001
目 录
修改记录 .................................................................................................................. 1
目 录 .................................................................................................................. 2 C语言编码规范 ....................................................................................................... 4 1 总则 ................................................................................................................ 4 1.1 目的............................................................................................................. 4 1.2 适用范围 ..................................................................................................... 4 1.3 术语定义 ..................................................................................................... 4 2 程序的布局 .................................................................................................... 4 2.1 文件头说明 ................................................................................................. 5 2.2 头文件说明 ................................................................................................. 6
2.2.1 条件编译 .............................................................................................. 6
2.2.2 头文件内容 .......................................................................................... 6 2.3 头文件布局示例 ......................................................................................... 7 2.4 源文件布局示例 ......................................................................................... 9 3 命名规则 ....................................................................................................... 11 3.1 匈牙利记名法 ............................................................................................ 11
3.1.1 基本命名规则 ..................................................................................... 11
3.1.2 细则 ..................................................................................................... 11 3.2 休斯编码规范 ........................................................................................... 14
3.2.1 基本命名规则 .................................................................................... 14
3.2.2 细则 .................................................................................................... 14 4 函数 .............................................................................................................. 16 4.1 函数的设计 ............................................................................................... 16
4.1.1 规则 .................................................................................................... 16
4.1.2 建议 .................................................................................................... 17 4.2 函数参数 ................................................................................................... 18
4.2.1 规则 .................................................................................................... 18
4.2.2 建议 .................................................................................................... 19 4.3 函数体 ....................................................................................................... 21
4.3.1 规则 .................................................................................................... 21
4.3.2 建议 .................................................................................................... 22 4.4 函数返回值 ............................................................................................... 25
4.4.1 规则 .................................................................................................... 25
4.4.2 建议 .................................................................................................... 25 5 变量 .............................................................................................................. 26 5.1 规则........................................................................................................... 26 5.2 建议........................................................................................................... 26 6 结构 .............................................................................................................. 27 6.1 规则........................................................................................................... 27 6.2 建议........................................................................................................... 28 7 注释 .............................................................................................................. 29
第 2 页 共 58 页
HX/QI/012001
7.1 规则........................................................................................................... 29 7.2 建议........................................................................................................... 32 8 排版 .............................................................................................................. 33 8.1 规则........................................................................................................... 33 8.2 建议........................................................................................................... 37 9 可读性 .......................................................................................................... 38 10 数据类型 ...................................................................................................... 39 11 编译、测试、发布 ....................................................................................... 40 11.1 规则 ....................................................................................................... 40 11.2 建议 ....................................................................................................... 41 12 断言 .............................................................................................................. 42 12.1 规则 ....................................................................................................... 42 12.2 建议 ....................................................................................................... 43 13 程序效率 ...................................................................................................... 45 13.1 规则 ....................................................................................................... 45 13.2 建议 ....................................................................................................... 47 14 内存操作 ...................................................................................................... 49
14.1 规则 ....................................................................................................... 49
14.2 建议 ....................................................................................................... 51 15 宏 .................................................................................................................. 52
16 其它 .............................................................................................................. 53
16.1 规则 ....................................................................................................... 53 16.2 建议 ....................................................................................................... 55 17 修改代码注意事项 ....................................................................................... 56 18 结语 .............................................................................................................. 57
第 3 页 共 58 页
HX/QI/012001
C语言编码规范
1 总则
1.1 目的
为了提高源程序的质量和可维护性,提高产品质量,我们有必要对产品源程序的编写风格做出统一的规范。
1.2 适用范围
本规范适用安全产品部生产的所有源程序。对于外购的
协议
离婚协议模板下载合伙人协议 下载渠道分销协议免费下载敬业协议下载授课协议下载
栈等其他源代码,修改时应遵循其自身的风格。
本规范的大部分是独立于具体语言的编程规则的总的原则,考虑到本部门主要使用的编程语言为标准C,所以部分规则专门针对标准C。
1.3 术语定义
本规范的示例都以C语言为背景,采用以下的术语描述:
? 规则:编程时强制必须遵守的原则。
? 建议:编程时必须加以考虑的原则。
? 说明:对此规则或建议进行必要的解释。
? 示例:对此规则或建议从正、反两个方面给出例子。
2 程序的布局
程序员经常忽略程序的布局,不重视程序的布局,实际上,程序的布局非常重要,首先它可以增加程序代码的可读性,并且能够减少编译错误,因此,在我们的开发过程中,应该首先考虑的问题就是程序的布局。
, 建议2:程序建议按照以下顺序编写:
第 4 页 共 58 页
HX/QI/012001
头文件编写顺序 文件头
包含头文件(可选)
测试开关宏定义
类型定义(可选)
常量定义
其他宏定义
联合、枚举等定义
结构体定义
其他定义(可选)
子系统公共头文件
包含头文件 系统头文件
接口头文件
其他头文件
本模块头文件 源文件编写顺序 全局变量 全局外部变量
程序内部静态变量
函数原型 外部函数原型
内部函数原型
主程序及内部函数 说明:子系统公共头文件中包括子系统各模块需要用到的基础的系统头文件、平台头文件、调试用到的头文件等。
2.1 文件头说明
不管是什么文件,包括.C,.CPP,.H,.uc等文件都应该有文件头,文件头必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。如下所示:
/*************************************************
* Copyright (C), 2004-2009, Hongxu Tech. Co., Ltd.
* File name: // 文件名
* Module name: //模块名称
* Author//Date: // 作者及完成日期
第 5 页 共 58 页
HX/QI/012001
* Version: //版本
* Description: // 用于详细说明此程序文件完成的主要功能,与其他模块
//或函数的接口,输出值、取值范围、含义及参数间的控制、
//顺序、独立或依赖等关系
* Others: // 其它内容的说明
------------------History-----------------------------
* Modifier/Date: //修订人及修订日期
* Modify Reason: //修订原因
* Modification: //修订的内容和位置的简要说明
------------------------------------------------------
* Modifier/Date: //修订人及修订日期
* Modify Reason: //修订原因
* Modification: //修订的内容和位置的简要说明
------------------------------------------------------
…
*************************************************/
2.2 头文件说明
2.2.1 条件编译
在编译一个文件的时候,常会多次包含同一个文件,为了防止重复定义,应该采用条件编译,这里以demo.h为例说明:
#ifndef __DEMO_H
#define __DEMO_H
…
#endif
符号常数的名称由前两个下划线('__')以及文件名再加上'_H' 组成(如上图例),文件名中所有字母皆应大写。
2.2.2 头文件内容
源程序文件(C,CPP,ASM)中若包含头文件,则首先应包含系统提供的头文件,再包含自定义的头文件。原则上每一个软件模块至少定义一个头文件,引用这些头文件时使用相对路径。
第 6 页 共 58 页
HX/QI/012001
2.3 头文件布局示例
/*************************************************
* Copyright (C), 2004-2009, Hongxu Tech. Co., Ltd.
* File name: demo.h
* Module name: demo
* Author//Date: tom/2009/03/28 * Version: 1.0
* Description: 示例头文件
* Others:
------------------History-----------------------------
* Modifier/Date: tom/2009/03/28 * Modify Reason: creat
* Modification:
------------------------------------------------------
*************************************************/
#ifndef __DEMO_H
#define __DEMO_H
/* 如果这是子系统头文件的话可在此包含子系统各模块公用的头文件 不是的话建议不在此包含其他头文件*/
/*公共头文件*/
#include
#include
#include
#include
/* 调度系统头文件*/
#include "cps.h"
/* 调试打印头文件*/
#include "debug.h"
#include "trace.h"
/* 模式匹配头文件*/
#include "matching.h"
#include "rule_matching.h"
/* 测试开关宏定义 */
#define TEMPORARY_TEST 1
/* 类型定义 */
typedef unsigned char BITS;
第 7 页 共 58 页
HX/QI/012001
typedef unsigned long DWORD; typedef unsigned short WORD; typedef unsigned char BYTE; typedef unsigned char BOOLEAN; #define FLOAT float
#define INT8 char
#define INT16 short
#define INT32 int
#define UINT8 unsigned char #define UINT16 unsigned short #define UINT32 unsigned int #define UINT64 unsigned long long
#define VOID void
#define STATIC static
#define EXTERN extern
/* 常量定义 */
#define _LINUX_ 1
#define TRUE 1
#define FALSE 0
#define TEST_STRING “just for test”
/* 其他宏定义 */
#define MEMBER_OFFSET( structure, member ) \
( ( int ) & ( ( ( structure * ) 0 ) -> member ) )
/* 联合、枚举等定义 */
typedef enum
{
FullFlag_UnFull = 0,
FullFlag_Full,
}FullFlag_Type;
/* 结构体定义 */
/* 结构体定义建议采用统一的字节对齐方式 */ typedef struct _ccToDRStat_ {
UINT8 useFlag;
INT32 connectFd;
UINT32 boardIp;
UINT32 dataFlow;
UINT8 flowError;
UINT8 timeOfFlowError;
UINT8 todySendFlowError;
第 8 页 共 58 页
HX/QI/012001
}PACKED ccToDrStat_t;
/* 其他定义 */
#endif
2.4 源文件布局示例
/*************************************************
* Copyright (C), 2004-2009, Hongxu Tech. Co., Ltd.
* File name: demo.c
* Module name: demo
* Author//Date: tom/2009/03/28 * Version: 1.0
* Description: 示例源文件
* Others:
------------------History-----------------------------
* Modifier/Date: tom/2009/03/28 * Modify Reason: creat
* Modification:
------------------------------------------------------
*************************************************/
#ifdef HAVE_DEMO /* 条件编译, 开关一般放在makefile中 */
/* 子系统公共头文件*/
#include "demo_common.h"
/* 系统头文件*/
#ifdef _LINUX_
#include "pthread.h"
#endif
/* 接口头文件*/
#include "demo_interface.h"
/* 本模块头文件*/
#include "demo.h"
/* 外部全局变量*/
extern UINT32 g_demo_num;
第 9 页 共 58 页
HX/QI/012001
/* 内部静态变量 */
UINT16 g_local_globle;
/* 外部函数 */
extern void ipConversion( UINT8 *deviceName, INT32 ip ); extern void freeDrHttpWapData( dr_http_data_t* httpwapData );
/* 内部函数 */
/* 主流程函数*/
void startDrCcThread( void * );
static BOOLEAN drRcvMsg( INT32 Fd );
/* 其他功能函数 */
static UINT32 drObtainIp( );
/*********************************************************** * Function: DrRecvMsgCtrl
* Description: 收消息线程调度入口函数
* Input: state, 当前线程模块的工作状态
* event, 消息类新
* pid, 当前线程模块的线程id
* *data, 接收到的消息指针
* Output: N/A
* Return: N/A
* Others:
------------------History----------------------------- * No.: 0
* Modifier/Date: tom/2009/03/28
* Modify Reason: creat
* Modification:
------------------------------------------------------ ************************************************************/ void
DrRecvMsgCtrl( int state, int event, int pid, void *data ) {
……;
}
#endif
第 10 页 共 58 页
HX/QI/012001
3 命名规则
根据开发平台的不同,在进行编码时,可以采用两套编码规范中的一种:休斯编码规范、匈牙利记名法。但是针对同一个产品只能够使用同一种编码规范。
另外如果开发的源代码是与商业代码或者操作系统源码紧耦合的,可以遵循该商业代码或者操作系统源码的命名规则。
以下分别对两种规范的命名规则进行基本的说明。
3.1 匈牙利记名法
3.1.1 基本命名规则
以下的编码规范以匈牙利记名法为基准进行说明,休斯编码规范与其有不同之处时将用红色字特别说明。
以下为基本的命名规则:
, 规则3-1-1:名字以小写字母的前缀开始,后面带若干英语单词或单词
的缩写,每个单词的第一个字母为大写,其余为小写,如iArrayIndex;
, 规则3-1-2:单词必须是有意义的,拒绝毫无意义的词如xxx,YYY2等;
, 规则3-1-3:名字的长度不宜过长,也不宜过短,一般为10,16个字母
为宜;
, 规则3-1-4:单词的字母数在5,6个以上时,可采用缩写,缩写的原则
是保留第一个字母,然后省略中间的元音字母及某些无需发音的辅音字
母如r等。如将current缩写为crnt,将response缩写为rsp,将control
缩写为ctrl等。
3.1.2 细则
3.1.2.1 函数命名规则
, 规则3-1-5:函数名采用“大小写隔开”方式。
, 规则3-1-6:函数命名形式为:模块名+有意义的名字(或缩写)(注:
第 11 页 共 58 页
HX/QI/012001
最好为动宾结构或状态/事件格式)。即函数名需具备自我解释的功能。 , 规则3-1-7:函数名字符串首字符不大写,其中如有缩写,仅大写缩写
的首字符。
, 规则3-1-8:对提供给大部分模块用的一些公用函数,不在以上规定中,
可全部用大写。
示例:
gtpProcessMsgFromSgsn(……);
gtpBuildCreatePdpContext(…..);
3.1.2.2 变量命名规则
, 规则3-1-9:变量的命名规范采用 “大写隔开”的方式,不要使用大写
隔开与下划线混排的方式。
, 规则3-1-10:变量名字符串首字符不大写,其中如有缩写,仅大写缩写
的首字符。
示例:
UINT32 teidu;
UINT32 packFiltId;
, 规则3-1-11:对于变量命名,禁止取单个字符(如i、j、k...),但i、
j、k作局部循环变量是允许的。示例:INT16 count表示带符号短整型
的计数器变量,该变量是局部变量。
, 规则3-1-12:局部变量(包括函数参数)的前缀按照变量的类型来定。
常用变量类型的前缀定义如下表所示:
类型 前缀 指针前缀
BITS bt
BOOL bl
char c pc
BYTE b pb
WORD w pw
DWORD dw pdw
INT n pn
VOID v pv
ENUM en pen
STRUCT t pt
LONG l pl
第 12 页 共 58 页
HX/QI/012001
数组 类型前缀+a,如ba
句柄 h
文件指针 fp
举例如下:
BOOL blRst;
char cInput;
BYTE bState, pbState;
WORD wEvent, pwEvent;
DWORD dwTimerId;
mFuncRet_T tFuncRet, ptFuncRet;
, 规则3-1-13:为保证可移植性,使用经过封装的基本数据类型。 说明:基本类型如下表
类型 说明
UINT8,UINT16,UINT32 无符号8位,16位,32位整型
INT8,INT16,INT32 带符号8位,16位,32位整型 示例:
/* define a counter varible ranges between 0 and 255 */
UINT8 count;
, 规则3-1-14:全局变量采用“模块名缩写+有意义的名字(或缩写)”。
3.1.2.3 结构,宏命名规则
, 规则3-1-15:自定义类型命名规则如下表:
构造类型 命名方式 举例
结构体 有意义的名字(或缩写)+_t pack_filt_t
联合体 有意义的名字(或缩写)+_u call_app_u
枚举类 有意义的名字(或缩写)+_e form_e 示例:
/*the keyword struct must follows a customized */
typedef struct _packFilt_t
{ /* name with the prefix _ */ UINT32 teidu;
UINT32 packFiltId;
struct _packFilt_t* pNext;
struct _packFilt_t* pPre; } packFilt_t;
packFilt_t packFilt;
packFilt_t* pPackFilt;
第 13 页 共 58 页
HX/QI/012001
3.2 休斯编码规范
3.2.1 基本命名规则
以下的编码规范以匈牙利记名法为基准进行说明,休斯编码规范与其有不同之处时将用红色字特别说明。
以下为基本的命名规则:
, 规则3-2-1:名字以小写以及下划线隔开的方式,如i_array_index;
, 规则3-2-2:单词必须是有意义的,拒绝毫无意义的词如xxx,YYY2等;
, 规则3-2-3:名字的长度不宜过长,也不宜过短,一般为10,16个字母
为宜;
, 规则3-2-4:单词的字母数在5,6个以上时,可采用缩写,缩写的原则
是保留第一个字母,然后省略中间的元音字母及某些无需发音的辅音字
母如r等。如将current缩写为crnt,将response缩写为rsp,将control
缩写为ctrl等。
3.2.2 细则
3.2.2.1 函数命名规则
, 规则3-2-5:函数名采用“大小写隔开”方式。
, 规则3-2-6:函数命名形式为:模块名+有意义的名字(或缩写)(注:
最好为动宾结构或状态/事件格式)。
, 规则3-2-7:函数名字符串首字符不大写,其中如有缩写,仅大写缩写
的首字符。
, 规则3-2-8:对提供给大部分模块用的一些公用函数,不在以上规定中,
可全部用大写。
示例:
gtpProcessMsgFromSgsn(……);
gtpBuildCreatePdpContext(…..);
第 14 页 共 58 页
HX/QI/012001
3.2.2.2 变量命名规则
, 规则3-2-9:变量的命名规范采用小写加下划线的方式。举例:
U8 pack_filt_id , 规则3-2-10:变量名字符串首字符不大写,其中如有缩写,仅大写缩写
的首字符。
示例:
U32 teidu;
U32 packFiltId; , 规则3-2-11:对于变量命名,禁止取单个字符(如i、j、k...),但i、
j、k作局部循环变量是允许的。示例:INT16 count表示带符号短整型
的计数器变量,该变量是局部变量。
, 规则3-2-12:局部变量(包括函数参数)的前缀按照变量的类型来定。
常用变量类型的前缀定义如下表所示:
类型 前缀 指针前缀
BITS bt
BOOL bl
char c pc
BYTE b pb
WORD w pw
DWORD dw pdw
INT n pn
VOID v pv
ENUM en pen
STRUCT t pt
LONG l pl
数组 类型前缀+a,如ba
句柄 h
文件指针 fp 举例如下:
BOOL blRst;
char cInput;
BYTE bState, pbState; WORD wEvent, pwEvent; DWORD dwTimerId;
mFuncRet_T tFuncRet, ptFuncRet;
, 规则3-2-13:为保证可移植性,使用经过封装的基本数据类型。
第 15 页 共 58 页
HX/QI/012001
说明:基本类型如下表
类型 说明
U8,U16,U32 无符号8位,16位,32位整型
INT8,INT16,INT32 带符号8位,16位,32位整型 示例:
/* define a counter varible ranges between 0 and 255 */
U8 count;
, 规则3-2-14:全局变量采用“模块名缩写+有意义的名字(或缩写)”。 示例:
INT32 g_modulename_mailnum;
3.2.2.3 结构,宏命名规则
, 规则3-2-15:自定义类型命名采用下划线分割单词。规则为:有意义的
名字(或缩写)+_t
/*the keyword struct must follows a customized */
typedef struct _pack_filt_t
{ /* name with the prefix _ */
UINT32 teidu;
UINT32 pack_filt_id;
struct _pack_filt_t* p_next;
struct _pack_filt_t* p_pre;
}pack_filt_t;
pack_filt_t pack_filt;
pack_filt_t* p_pack_filt;
4 函数
4.1 函数的设计
4.1.1 规则
, 规则4-1-1:一个函数仅完成一件功能。
, 规则4-1-2:明确函数功能,精确(而不是近似)地实现函数设计。 , 规则4-1-3:不要设计多用途面面俱到的函数。
说明:多功能集于一身的函数,很可能使函数的理解、测试、维护等变得
第 16 页 共 58 页
HX/QI/012001
困难。
, 规则4-1-4:设计高扇入、合理扇出(小于7)的函数。
说明:扇出是指一个函数直接调用(控制)其它函数的数目,而扇入是指有多少上级函数调用它。
扇出过大,表明函数过分复杂,需要控制和协调过多的下级函数;而扇出过小,如总是1,表明函数的调用层次可能过多,这样不利程序阅读和函数结构的
分析
定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析
,并且程序运行时会对系统资源如堆栈空间等造成压力。函数较合理的扇出(调度函数除外)通常是3-5。扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数进一步分解多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。
扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。
较良好的软件结构通常是顶层函数的扇出较高,中层函数的扇出较少,而底层函数则扇入到公共模块中。
4.1.2 建议
, 建议4-1-1:函数的规模尽量限制在300行以内。
说明:不包括注释和空格行。
, 建议4-1-2:为简单功能编写函数。
说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。
示例:
如下语句的功能不很明显。
value = ( a > b ) ? a : b ;
改为如下就很清晰了。
INT32 max (INT32 a, INT32 b)
{
return ((a > b) ? a : b);
}
第 17 页 共 58 页
HX/QI/012001
value = max (a , b);
或改为如下。
#define MAX (a , b) (((a) > (b)) ? (a) : (b))
value = MAX (a , b);
, 建议4-1-3:改进模块中函数的结构,降低函数间的耦合度,并提高函
数的独立性以及代码可读性、效率和可维护性。优化函数结构时,要遵
守以下原则:
(1)不能影响模块功能的实现。
(2)仔细考查模块或函数出错处理及模块的性能要求并进行完善。
(3)通过分解或合并函数来改进软件结构。
(4)考查函数的规模,过大的要进行分解。
(5)降低函数间接口的复杂度。
(6)不同层次的函数调用要有较合理的扇入、扇出。
(7)函数功能应可预测。
(8)提高函数内聚。(单一功能的函数内聚最高)
说明:对初步划分后的函数结构应进行改进、优化,使之更为合理。 4.2 函数参数
4.2.1 规则
, 规则4-2-1:函数参数不应过多,不得超过8个,否则应当考虑函数功
能的压缩。
说明:目的减少函数间接口的复杂度。
-2-2:接口函数本身要对接口函数参数的合法性进行检查。 , 规则4
说明:对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,
第 18 页 共 58 页
HX/QI/012001
降低了效率。我们规定,对接口函数入口参数的合法性检查及错误保护由函数自身完成。
, 规则4-2-3:防止将函数的参数作为工作变量。
说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
示例:
以下函数的实现不太好。
void sumData ( INT32 num, INT32 *pData, INT32 *pSum )
{
INT32 count;
*pSum = 0;
for (count = 0; count < num; count++)
{
*pSum += pData[count]; // pSum成了工作变量,不太
好。
}
}
若改为如下,则更好些。
void sumData ( INT32 num, INT32 *pData, INT32 *pSum )
{
INT32 count ;
INT32 sumTemp;
sumTemp = 0;
for (count = 0; count < num; count ++)
{
sumTemp += pData[count];
}
*pSum = sumTemp;
}
4.2.2 建议
, 建议4-2-1:应避免冗余参数,如果一个函数只用到结构中的几个成员
第 19 页 共 58 页
HX/QI/012001
作为参数,则应该将这几个成员变量作为参数带入,而不应该将整个结
构作为参数带入。
, 建议4-2-2:当结构变量作为参数时,为避免堆栈和数据复制的开销,
应该尽量传送结构的指针而不是整个结构体。
, 建议4-2-3:非调度函数应减少或防止控制参数,尽量只使用数据参数。 说明:本建议目的是防止函数间的控制耦合。调度函数是指根据输入的消息类型或控制命令,来启动相应的功能实体(即函数或过程),而本身并不完成具体功能。控制参数是指改变函数功能行为的参数,即函数要根据此参数来决定具体怎样工作。非调度函数的控制参数增加了函数间的控制耦合,很可能使函数间的耦合度增大,并使函数的功能不唯一。
示例:
如下函数构造不太合理。
INT32 addSub ( INT32 a, INT32 b, INT8 addSubFlg )
{
if (addSubFlg == INTEGER_ADD)
{
return (a + b);
}
else
{
return (a – b);
}
}
不如分为如下两个函数清晰。
INT32 add ( INT32 a, INT32 b )
{
return (a + b);
}
INT32 Sub ( INT32 a, INT32 b )
{
return (a – b);
}
, 建议4-2-4:在调用函数填写参数时,应尽量减少没有必要的默认数据
类型转换或强制数据类型转换。
说明:因为数据类型转换或多或少存在危险。
第 20 页 共 58 页
HX/QI/012001
4.3 函数体
4.3.1 规则
, 规则4-3-1:函数的功能应该是可以预测的,也就是只要输入数据相同
就应产生同样的输出。
说明:带有内部“存储器”的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样的函数既不易于理解又不利于测试和维护。在C语言中,函数的static局部变量是函数的内部存储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是STATIC的局部变量的地址作为返回值,若为AUTO类,则返回为错针。
示例:如下函数,其返回值(即功能)是不可预测的。
INT32 integerSum ( INT32 base )
{
INT32 index;
static INT32 sum = 0; // 注意,是static类型的。
// 若改为auto类型,则函数即变为可预测。
for (index = 1; index <= base; index++)
{
sum += index;
}
return sum;
}
, 规则4-3-2:不要编写依赖于其他函数内部实现的函数。
说明:此条为函数独立性的基本要求。由于目前大部分高级语言都是结构化的,所以通过具体语言的语法要求与编译器功能,基本就可以防止这种情况发生。但在汇编语言中,由于其灵活性,很可能使函数出现这种情况。
示例:如下是在DOS下TASM的汇编程序例子。过程PrintMsg的实现依赖于InputMsg的具体实现,这种程序是非结构化的,难以维护、修改。
... // 程序代码
proc PrintMsg // 过程(函数)PrintMsg
... // 程序代码
jmp LABEL
... // 程序代码
第 21 页 共 58 页
HX/QI/012001
endp
proc InputMsg // 过程(函数)InputMsg
... // 程序代码
LABEL:
... // 程序代码
endp
, 规则4-3-3:检查函数所有参数输入的有效性。
, 规则4-3-4:检查函数所有非参数输入的有效性,如数据文件、公共变
量等。
说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。函数在使用输入之前,应进行必要的检查。 , 规则4-3-5:避免函数中不必要语句,防止程序中的垃圾代码。
说明:程序中的垃圾代码不仅占用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的测试、维护等造成不必要的麻烦。 4.3.2 建议
, 建议4-3-1:编写可重入函数时,应注意局部变量的使用(如编写C语
言的可重入函数时,应使用auto即缺省态局部变量或寄存器变量)。
说明:编写C语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。
, 建议4-3-2:编写可重入函数时,若使用全局变量,则应通过关中断、
信号量(即P、V操作)等手段对其加以保护。
说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。
示例:假设exam是int32型全局变量,函数squreExam返回exam平方值。那么如下函数不具有可重入性。
INT32 example ( INT32 para )
{
INT32 temp;
exam = para; // (**)
temp = squareExam( );
第 22 页 共 58 页
HX/QI/012001
return temp;
}
此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使exam赋与另一个不同的para值,所以当控制重新回到“temp = squareExam( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改进。
INT32 example ( INT32 para )
{
INT32 temp;
[申请信号量操作]
exam = para;
temp = squareExam ( );
[释放信号量操作]
return temp;
}
, 建议4-3-3:让函数在调用点显得易懂、容易理解。
, 建议4-3-4:防止把没有关联的语句放到一个函数中。
说明:防止函数或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程中。随机内聚给函数或过程的维护、测试及以后的升级等造成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境。
在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。
示例:
如下函数就是一种随机内聚。
void initVar( void )
{
rect.length = 0;
第 23 页 共 58 页
HX/QI/012001
rect.width = 0; /* 初始化矩形的长与宽 */
point.x = 10;
point.y = 10; /* 初始化“点”的坐标 */
}
矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。
应如下分为两个函数:
void initRect( void )
{
rect.length = 0;
rect.width = 0; /* 初始化矩形的长与宽 */
}
void initPoint( void )
{
point.x = 10;
point.y = 10; /* 初始化“点”的坐标 */
}
, 建议4-3-5:应该尽量减少冗余代码的出现,如果有五行以上的连续代
码重复出线3次以上,应该在不增加程序复杂度的情况下,将其独立成
函数,从而增强程序的可维护性。
, 建议4-3-6:功能不明确的较小的函数,特别是仅有一个上级函数调用
它时,应考虑把它合并到上级函数中,而不必单独存在。 说明:模块中函数划分的过多,一般会使函数间的接口变得复杂。所以过小的函数,特别是扇入很低的或功能不明确的函数,不值得单独存在。 , 建议4-3-7:函数本身或函数间尽量不要使用递归调用。 说明:递归调用特别是函数间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。
, 建议4-3-8:在多任务操作系统的环境下编程,要注意函数可重入性的
构造。
第 24 页 共 58 页
HX/QI/012001
说明:可重入性是指函数可以被多个任务进程调用。在多任务操作系统中,函数是否具有可重入性是非常重要的,因为这是多个进程可以共用此函数的必要条件。另外,编译器是否提供可重入函数库,与它所服务的操作系统有关,只有操作系统是多任务时,编译器才有可能提供可重入函数库。如DOS下BC和MSC等就不具备可重入函数库,因为DOS是单用户单任务操作系统。 , 建议4-3-9:当一个过程(函数)中对较长变量(一般是结构的成员)
有较多引用时,可以用一个意义相当的宏代替。
说明:这样可以增加编程效率和程序的可读性。
示例:
在某个过程中较多引用
theReceiveBuffer[firstSocket].pByDataPtr,
则可以通过以下宏定义来代替:
# define pSOCKDATA \
theReceiveBuffer[firstScoket].pByDataPtr
4.4 函数返回值
4.4.1 规则
, 规则4-4-1:函数的返回值要清楚、明了,让使用者不容易忽视错误情
况。
说明:函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。
, 规则4-4-2:对所调用函数的错误返回码要仔细、全面地处理。 4.4.2 建议
, 建议4-4-1:除非必要,最好不要把与函数返回值类型不同的变量,以
编译系统默认的转换方式或强制的转换方式作为返回值返回。 , 建议4-4-2:对于提供了返回值的函数,在引用时最好使用其返回值。
第 25 页 共 58 页
HX/QI/012001
5 变量
5.1 规则
, 规则5-1:去掉没必要的公共变量。
说明:公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以降低模块间的耦合度。
, 规则5-2:仔细定义并明确公共变量的含义、作用、取值范围及公共变
量间的关系。
说明:在对变量声明的同时,应对其含义、作用及取值范围进行注释说明,同时若有必要还应说明与其它变量的关系。
, 规则5-3:明确公共变量与操作此公共变量的函数或过程的关系,如访
问、修改及创建等。
说明:明确过程操作变量的关系后,将有利于程序的进一步优化、单元测试、系统联调以及代码维护等。这种关系的说明可在注释或文档中描述。 , 规则5-4:严禁使用未经初始化的变量作为右值。
说明:特别是在C中引用未经赋值的指针,经常会引起系统崩溃。 5.2 建议
, 建议5-1:当向公共变量传递数据时,要十分小心,防止赋与不合理的
值或越界等现象发生。
说明:对公共变量赋值时,若有必要应进行合法性检查,以提高代码的可靠性、稳定性。
, 建议5-2:防止局部变量与公共变量同名。
说明:若使用了较好的命名规则,那么此问题可自动消除。 , 建议5-3:使用严格形式定义的、可移植的数据类型,尽量不要使用与
具体硬件或软件环境关系密切的变量。
, 建议5-4:构造仅有一个模块或函数可以修改、创建,而其余有关模块
或函数只访问的公共变量,防止多个不同模块或函数都可以修改、创建
第 26 页 共 58 页
HX/QI/012001
同一公共变量的现象。降低公共变量耦合度。
6 结构
6.1 规则
, 规则6-1:结构的功能要单一,是针对一种事务的抽象。
说明:设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代
表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系
或关系很弱的不同事务的元素放到同一结构中。
示例:
如下结构不太清晰、合理。
typedef struct _student_t
{
INT8 name[8]; /* student's name */
INT8 age; /* student's age */
INT8 sex; /* student's sex, as follows */
/* 0 - FEMALE; 1 - MALE */
INT8 teacherName[8]; /* the student teacher's name
*/
INT8 teacherSex; /* his teacher sex */
} student_t;
若改为如下,可能更合理些。
typedef struct _teacher_t
{
INT8 name[8]; /* teacher name */
INT8 sex; /* teacher sex, as follows */
/* 0 - FEMALE; 1 - MALE */
} teacher_t;
typedef struct _student_t
{
INT8 name[8]; /* student's name */
INT8 age; /* student's age */
INT8 sex; /* student's sex, as follows */
/* 0 - FEMALE; 1 - MALE */
INT32 teacherInd; /* his teacher index */
} student_t;
, 规则6-2:不要设计面面俱到、非常灵活的数据结构。
第 27 页 共 58 页
HX/QI/012001
说明:面面俱到、灵活的数据结构反而容易引起误解和操作困难。
6.2 建议
, 建议6-1:不同结构间的关系不要过于复杂。
说明:若两个结构间关系较复杂、密切,那么应合为一个结构。 示例:
如下两个结构的构造不合理。
typedef struct _person_one_t {
INT8 name[8];
INT8 addr[40];
INT8 sex;
INT8 city[15];
} person_one_t;
typedef struct _person_two_t {
INT8 name[8];
INT8 age;
INT8 tel;
} person_two_t;
由于两个结构都是描述同一事物的,那么不如合成一个结构。 typedef struct _person_t {
INT8 name[8];
INT8 age;
INT8 sex;
INT8 addr[40];
INT8 city[15];
INT8 tel;
} person_t;
, 建议6-2:结构中元素的个数应适中。若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数。 说明:增加结构的可理解性、可操作性和可维护性。 示例:
假如认为如上的person_t结构元素过多,那么可如下对之划分。 typedef struct _person_base_info_t
第 28 页 共 58 页
HX/QI/012001
{
INT8 name[8];
INT8 age;
INT8 sex;
} person_base_info_t;
typedef struct _person_address_t
{
INT8 addr[40];
INT8 city[15];
INT8 tel;
} person_address_t;
typedef struct _person_t
{
person_base_info_t personBase;
person_address_t personAddr;
} person_t;
, 建议6-3:仔细设计结构中元素的布局与排列顺序,使结构容易理解、
节省占用空间,并减少引起误用现象。
, 建议6-4:结构的设计要尽量考虑向前兼容和以后的版本升级,并为某
些未来可能的应用保留余地(如预留一些空间等)。
说明:软件向前兼容的特性,是软件产品是否成功的重要标志之一。如果要想使产品具有较好的前向兼容,那么在产品设计之初就应为以后版本升级保留一定余地,并且在产品升级时必须考虑前一版本的各种特性。
7 注释
7.1 规则
, 规则7-1:注释格式统一使用“/* „„ */”。
, 规则7-2:不得在注释中使用缩写,特别是非常用缩写。 说明:在使用缩写时或之前,应对缩写进行必要的说明。 , 规则7-3:注释的内容要清楚、明了,含义准确,防止注释二义性。错
误的注释不但无益反而有害。
, 规则7-4:注释应与其描述的代码位置相近,对代码的注释应放在其上
方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方
第 29 页 共 58 页
HX/QI/012001
则需与其上面的代码用空行隔开。
, 规则7-5:对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方相邻位置或右方。
示例:
/* active statistic task number */ #define MAX_ACT_TASK_NUMBER 1000
, 规则7-6:全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。
示例:
/* The ErrorCode when SCCP translate */
/* Global Title failure, as follows */ // 变量作用、含义 /* 0 , SUCCESS 1 , GT Table error */ /* 2 , 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() */ // 使用方法 INT8 gtTranErrorCode;
, 规则7-7:对变量的定义和分支语句(条件分支、循环语句等)必须编写注释。
说明:这些语句往往是程序实现某一特定功能的关键,对于维护人员来说,
良好的注释帮助更好的理解程序,有时甚至优于看设计文档。
, 规则7-8:对于switch语句下的case语句,如果因为特殊情况需要处理完一个case后进入下一个case处理,必须在该case语句处理完、下一个case语句前加上明确的注释。
说明:这样比较清楚程序编写者的意图,有效防止无故遗漏break语句。 示例(注意斜体加粗部分):
case CMD_UP:
processUp();
break;
case CMD_DOWN:
processDown();
break;
第 30 页 共 58 页
HX/QI/012001
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;
...
, 规则7-9:函数头部应进行注释,列出:函数的目的/功能、输入参数、
输出参数、返回值等。
示例:下面这段函数的注释比较标准,当然,并不局限于此格式,但上述
信息建议要包含在内。
/****************************************************************
* Function: // 函数名称
* Description: // 函数功能、性能等的描述 * Input: // 输入参数说明,包括每个参数的作
// 用、取值说明及参数间关系。 * Output: // 对输出参数的说明。 * Return: // 函数返回值的说明
第 31 页 共 58 页
HX/QI/012001
* Others: // 其它说明
* ------------------History-----------------------------
* No.: 0
* Modifier/Date: tom/2009/03/28
* Modify Reason: creat
* Modification:
------------------------------------------------------
*****************************************************************/
, 规则7-10:不能在一行代码或表达式的中间插入注释。
, 规则7-11:数据结构声明(包括数组、结构、类、枚举等),如果其命名
不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相
邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。
示例:可按如下形式说明枚举/联合/结构。
/* sccp interface with sccp user primitive message name */
typedef enum _sccpUserPrimitive_e
{
N_UNITDATA_IND, /* sccp notify sccp user unit data come */
N_NOTICE_IND, /* sccp notify user the No.7 network can not */
/* transmission this message */
N_UNITDATA_REQ, /* sccp user's unit data transmission request*/
} sccpUserPrimitive_e;
, 规则7-12:每个模块模块的注释量的下限为10%。
7.2 建议
, 建议7-1:边写代码边注释,修改代码同时修改相应的注释,以保证注
释与代码的一致性。不再有用的注释要删除。
, 建议7-2:通过对函数或过程、变量、结构等正确的命名以及合理地组
织代码的结构,使代码成为自注释的。
说明:清晰准确的函数、变量等的命名,可增加代码可读性,并减少不必
要的注释。
, 建议7-3:在代码的功能、意图层次上进行注释,提供有用、额外的信
息。
说明:注释的目的是解释代码的目的、功能和采用的方法,提供代码以外
第 32 页 共 58 页
HX/QI/012001
的信息,帮助读者理解代码,防止没必要的重复注释信息。 示例:
如下注释意义不大。
/* if receiveFlag is TRUE */
if (receiveFlag)
而如下的注释则给出了额外有用的信息。
/* if mtp receive a message from links */
if (receiveFlag)
, 建议7-4:在程序块的结束行右方加注释标记,以表明某程序块的结束。 说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。
示例:参见如下例子。
if (...)
{
// program code
while (index < MAX_INDEX)
{
// program code
} /* end of while (index < MAX_INDEX) */ // 指明该条while语句结
束
} /* end of if (...)*/ // 指明是哪条if语句结束
, 建议7-5:注释统一采用中文注释,避免对注释理解的差异造成维护困
难。
8 排版
8.1 规则
, 规则8-1:程序块要采用缩进风格编写。建议用SourceInsight编写代
码,用F9、F10控制缩进。
示例:
main()
{
if (……)
{
while (……)
{
第 33 页 共 58 页
HX/QI/012001
…..
}
}
}
, 规则8-2:相对独立的程序块之间、变量说明之后必须加空行。 示例:
如下例子不符合规则。
INT32 linkRet;
if (isResFail(linkRet))
{
…/* program code */
}
应如下书写
INT32 linkRet;
if (isResFail (linkRet)) {
…/* program code */
}
, 规则8-3:每行语句横向不超过78个字符,以一屏为限。如果逻辑表达式或算术表达式过长,超出屏幕宽度时应折到下一行,保持表达式对齐,并且将折行处的逻辑运算符或算术运算符留在上一行末尾,表示本语句未结束。如下例:
if( (ICallNO<0) || (ICallNO>=MAX_CALL_NUM) ||
(IStatusReportNO<0) ||
(IStatusReportNO>=STATUS_REPORT_NUM) )
{…}
, 规则8-4:不允许把多个短语句写在一行中,即一行只写一条语句。 示例:
如下例子不符合规范。
rect.length = 0; rect.width = 0;
应如下书写
rect.length = 0;
rect.width = 0;
, 规则8-5:每一个开括号"{" 都必须单独占一行, 并且同与它相关联的控制结构(if、while、for、switch)的边界对齐。
第 34 页 共 58 页
HX/QI/012001
, 规则8-6:每一个闭括号"}" 都必须单独占一行, 并且同与它相关联的
控制结构(if,while,for,switch)的边界对齐,并给出相应的注释表明
结构体结束。本条规则的唯一例外是do-while结构。在该结构中,闭括
号必须与while (condition)位於同一行中, 以免使别人把while误解
为空循环。
do
{
...
} while(logical expression); , 规则8-7:控制结构体内的语句要从"{" 的下一行比结构起始行缩进2
个空格直至结构体结束的"}" 前为止。
, 规则8-8:函数的参数在书写时如果一行写不下,则采用纵向对齐的方
式,见下例:
BYTE FunctionName( T_FirstPara IFirstPara,
T_SecondPara ISecondPara,
T_ThirdPara OThirdPara )
{
…
}
, 规则8-9:对齐只使用空格键,不使用TAB键。
说明:以免用不同的编辑器阅读程序时,因TAB键所设置的空格数目不同而造成程序布局不整齐,不要使用BC作为编辑器合版本,因为BC会自动将8个空格变为一个TAB键,因此使用BC合入的版本大多会将缩进变乱。 , 规则8-10:函数或过程的开始、结构的定义及循环、判断等语句中的代
码都要采用缩进风格,case语句下的情况处理语句也要遵从语句缩进要
求。
, 规则8-11:在两个以上的关键字、变量、常量进行对等操作时,它们之
间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是
关系密切的立即操作符(如,>和.),后不应加空格。
示例:
(1) 逗号、分号只在后面加空格。
INT32 a, b, c;
(2)比较操作符, 赋值操作符"="、 "+=",算术操作符"+"、"%",逻辑作
第 35 页 共 58 页
HX/QI/012001
符"&&"、"&",位域操作符"<<"、"^"等双目操作符的前后加空格。 if (currentTime >= MAX_TIME_VALUE)
a = b + c;
a *= 2;
a = b ^ 2;
(3)"!"、"~"、"++"、"--"、"&"(地址运算符)等单目操作符前后不加空 格。
*p = 'a'; // 内容操作"*"与内容之间
flag = !isEmpty; // 非操作"!"与内容之间
p = &mem; // 地址操作"&" 与内容之间 i++; // "++","--"与内容之间
(4)"->"、"."前后不加空格。
p->id = pid; // "->"指针前后不加空格
(5) if、for、while、switch等与后面的括号间应加空格,使if等关键字
更为突出、明显。
if ( ……)
, 规则8-12:注释与所描述内容进行同样的缩排。 说明:可使程序排版整齐,并方便注释的阅读与理解。 示例:
如下例子,排版不整齐,阅读稍感不方便。
void exampleFun( void ) {
/* code one comments */
CodeBlock One
/* code two comments */
CodeBlock Two
}
应改为如下布局。
void exampleFun( void ) {
/* code one comments */
CodeBlock One
/* code two comments */
CodeBlock Two
}
, 规则8-13:将注释与其上面的代码用空行隔开。
第 36 页 共 58 页
HX/QI/012001
示例:
如下例子,显得代码过于紧凑。
/* code one comments */
program code one
/* code two comments */
program code two
应如下书写
/* code one comments */
program code one
/* code two comments */
program code two
8.2 建议
, 建议8-1:连续的赋值运算推荐排列如下格式,把一组语句的等号对齐,等号的两边必须有空格:
bBillsDue = TRUE; NextCheckBook = 0;
BBillsPaid = FALSE;
, 建议8-2:若函数或过程中的参数较长,则要进行适当的划分。 示例:
statusCompare ((INT8 *)&status, (INT8 *)&(taskStatusTable[taskNo].statusRecord),
sizeof(stat_rec_t));
, 建议8-3:当控制结构体(if,while,for)中只有一条语句时,原则上也要由大括号括起, 以避免需要添加另外的语句时忘记加大括号。为了节省函数体行数在某些逻辑简单的情况下可以不使用大括号,但在添加语句时一定要将大括号加上。当且仅当语句体内容为空时,此规则可例外,但即便是空语句也必须单独位于一行中。如下二例所示。 if( expression )
{
return TRUE;
}
while( expression )
;
第 37 页 共 58 页
HX/QI/012001
, 建议8-4:每个相对完整的程序体结束后应间隔一行,以显示出段落性。
9 可读性
, 建议9-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)造成了判断条件出错。 , 建议9-2:避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。
示例:
如下的程序可读性差。
if (trunk[index].trunkState == 0) {
trunk[index].trunkState = 1;
... // program code
}
应改为如下形式。
#define TRUNK_IDLE 0
#define TRUNK_BUSY 1
if (trunk[index].trunkState == TRUNK_IDLE)
第 38 页 共 58 页
HX/QI/012001
{
trunk[index].trunkState = TRUNK_BUSY;
... // program code
}
, 建议9-3:源程序中关系较为紧密的代码应尽可能相邻。 说明:便于程序阅读和查找。
示例:
以下代码布局不太合理。
rectLength = 10;
pCharPoi = str;
rectWidth = 5;
若按如下形式书写,可能更清晰一些。
rectLength = 10;
rectWidth = 5; // 矩形的长与宽关系较密切,放在一起。 pCharPoi = str;
, 建议9-4:不要使用难懂的技巧性很高的语句,除非很有必要时。 说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。 示例:
如下表达式,考虑不周就可能出问题,也较难理解。
* pStatPoi ++ += 1;
* ++ pStatPoi += 1;
应分别改为如下。
*pStatPoi += 1;
pStatPoi++; //此二语句功能相当于“ * pStatPoi ++ += 1; ”
++ pStatPoi;
*pStatPoi += 1; //此二语句功能相当于“ * ++ pStatPoi += 1; ”
10 数据类型
, 建议10-1:留心具体语言及编译器处理不同数据类型的原则及有关细节。
说明:如在C语言中,static局部变量将在内存“数据区”中生成,而
非static局部变量将在“堆栈”中生成。这些细节对程序质量的保证非常重
第 39 页 共 58 页
HX/QI/012001
要。
, 建议10-2:编程时,要注意数据类型的强制转换。
说明:当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。 , 建议10-3:对编译系统默认的数据类型转换,也要有充分的认识。
示例:
如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。
INT8 chr;
INT16 i;
chr = -1;
i = chr; // 编译器不产生告警,此时i为0xFFFF。 , 建议10-4:尽量减少没有必要的数据类型默认转换与强制转换。 , 建议10-5:合理地设计数据并使用自定义数据类型,避免数据间进行不
必要的类型转换。
, 建议10-6:对自定义数据类型进行恰当命名,使它成为自描述性的,以
提高代码可读性。注意其命名方式在同一产品中的统一。
说明:使用自定义类型,可以弥补编程语言提供类型少、信息量不足的缺点,并能使程序清晰、简洁。
11 编译、测试、发布
11.1 规则
, 规则11-1:在代码编译调试时必须打开编译器的所有告警开关对程序进
行编译,同一模块必须统一编译开关选项,必须消除所有告警信息。 , 规则11-2:正式软件产品中应把断言及其它调测代码去掉(即把有关的
调测开关关掉)。
说明:加快软件运行速度。
, 规则11-3:在软件系统中设置与取消有关测试手段,不能对软件实现的
功能等产生影响。
说明:即有测试代码的软件和关掉测试代码的软件,在功能行为上应一致。
第 40 页 共 58 页
HX/QI/012001
, 规则11-4:用调测开关来切换软件的DEBUG版和正式版,而不要同时存
在正式版本和DEBUG版本的不同源文件,以减少维护的难度。 , 规则11-5:软件的DEBUG版本和发行版本应该统一维护,不允许分家,
并且要时刻注意保证两个版本在实现功能上的一致性。
11.2 建议
, 建议11-1:在编写代码之前,应预先设计好程序调试与测试的方法和手
段,并设计好各种调测开关及相应测试代码如打印函数等。
说明:程序的调试与测试是软件生存周期中很重要的一个阶段,如何对软件进行较全面、高率的测试并尽可能地找出软件中的错误就成为很关键的问题。因此在编写源代码之前,除了要有一套比较完善的测试
计划
项目进度计划表范例计划下载计划下载计划下载课程教学计划下载
外,还应设计出一系列代码测试手段,为单元测试、集成测试及系统联调提供方便。 , 建议11-2:调测开关应分为不同级别和类型。
说明:调测开关的设置及分类应从以下几方面考虑:针对模块或系统某部分代码的调测;针对模块或系统某功能的调测;出于某种其它目的,如对性能、容量等的测试。这样做便于软件功能的调测,并且便于模块的单元测试、系统联调等。
, 建议11-3:编写防错程序,然后在处理错误之后可用断言宣布发生错误。
示例:假如某模块收到通信链路上的消息,则应对消息的合法性进行检查,若消息类别不是通信协议中规定的,则应进行出错处理,之后可用断言报告,如下例。
#ifdef _EXAM_ASSERT_TEST_ // 若使用断言测试
/* Notice: this function does not call 'abort' to exit program */
void assertReport( INT8 * pFileName, UINT32 lineNo )
{
printf( "\n[EXAM]Error Report: %s, line %u\n",
pFileName, lineNo );
}
#define ASSERT_REPORT( condition )
if ( condition ) // 若条件成立,则无动作
NULL;
第 41 页 共 58 页
HX/QI/012001
else // 否则报告
assertReport ( __FILE__, __LINE__ )
#else // 若不使用断言测试
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
INT32 msgHandle( UINT8 msgName, UINT8 * pMsg )
{
switch( msgName )
{
case MSG_ONE:
... // 消息MSG_ONE处理
return MSG_HANDLE_SUCCESS;
... // 其它合法消息处理
default:
... // 消息出错处理
ASSERT_REPORT( FALSE ); // “合法”消息不成立,报
告
return MSG_HANDLE_ERROR;
}
}
12 断言(建议非必要不使用断言,以免误用)
12.1 规则
, 规则12-1:不能用断言来检查最终产品肯定会出现且必须处理的错误情
况。
说明:断言是用来处理不应该发生的错误情况的,对于可能会发生的且必须处理的情况要写防错程序,而不是断言。如某模块收到其它模块或链路上的消息后,要对消息的合理性进行检查,此过程为正常的错误检查,不能用断言来实现。
, 规则12-2:对较复杂的断言加上明确的注释。
说明:为复杂的断言加注释,可澄清断言含义并减少不必要的误用。
第 42 页 共 58 页
HX/QI/012001
12.2 建议
, 建议12-1:在以下几种情况下推荐使用断言:
(1)进程进入不该进入的状态.
(2)进程收到不期望收到的事件.
(3)输入参数为空指针.
(4)在if/else语句中对没有列举到的情况的处理.
(5)在switch/default语句中对缺省状态,事件或参数的处理. (6)接口函数对输入参数的有效性判断.
, 建议12-2:使用断言来发现软件问题,提高代码可测性。 说明:断言是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位软件问题,同时对系统错误进行自动报警。断言可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。实际应用时,可根据具体情况灵活地设计断言。
示例:下面是C语言中的一个断言,用宏来设计的。(其中NULL为0L)
#ifdef _EXAM_ASSERT_TEST_ // 若使用断言测试
void examAssert( INT8 * pfileName, UINT32 lineNo )
{
printf( "\n[EXAM]Assert failed: %s, line %u\n",
pfileName, lineNo );
abort( );
}
#define EXAM_ASSERT( condition )
if (condition) // 若条件成立,则无动作
NULL;
else // 否则报告
examAssert( __FILE__, __LINE__ )
#else // 若不使用断言测试
#define EXAM_ASSERT(condition) NULL
#endif /* end of ASSERT */
第 43 页 共 58 页
HX/QI/012001
, 建议12-3:用断言来检查程序正常运行时不应发生但在调测时有可能发
生的非法情况。
, 建议12-4:用断言确认函数的参数。
示例:假设某函数参数中有一个指针,那么使用指针前可对它检查,如下。
INT32 examFun( INT8 *pStr )
{
EXAM_ASSERT( pStr != NULL ); // 用断言检查“假设指针不为
空”这个条件
... //other program code
}
, 建议12-5:用断言保证没有定义的特性或功能不被使用。 示例:假设某通信模块在设计时,准备提供“无连接”和“连接” 这两种业务。但当前的版本中仅实现了“无连接”业务,且在此版本的正式发行版中,用户(上层模块)不应产生“连接”业务的请求,那么在测试时可用断言检查用户是否使用“连接”业务。如下。
#define EXAM_CONNECTIONLESS 0 // 无连接业务
#define EXAM_CONNECTION 1 // 连接业务
INT32 msgProcess(exam_message_t *pMsg )
{
INT8 service; /* message service class */
EXAM_ASSERT( pMsg != NULL );
service = getMsgServiceClass( pMsg );
EXAM_ASSERT( service != EXAM_CONNECTION ); // 假设不
使用连接业务
... //other program code
}
, 建议12-6:用断言对程序开发环境(OS/Compiler/Hardware)的假设进
行检查。
说明:程序运行时所需的软硬件环境及配置要求,不能用断言来检查,而必须由一段专门代码处理。用断言仅可对程序开发环境中的假设及所配置的某版本软硬件是否具有某种功能的假设进行检查。如某网卡是否在系统运行环境
第 44 页 共 58 页
HX/QI/012001
中配置了,应由程序中正式代码来检查;而此网卡是否具有某设想的功能,则可由断言来检查。
对编译器提供的功能及特性假设可用断言检查,原因是软件最终产品(即运行代码或机器码)与编译器已没有任何直接关系,即软件运行过程中(注意不是编译过程中)不会也不应该对编译器的功能提出任何需求。
示例:用断言检查编译器的INT16型数据占用的内存空间是否为2,如下。
EXAM_ASSERT( sizeof( INT16 ) == 2 ); 13 程序效率
13.1 规则
, 规则13-1:局部效率应为全局效率服务,不能因为提高局部效率而对全
局效率造成影响。
, 规则13-2:通过对系统数据结构的划分与组织的改进,以及对程序算法
的优化来提高空间效率。
说明:这种方式是解决软件空间效率的根本办法。
示例:如下记录学生学习成绩的结构不合理。
typedef struct _student_score_t
{
INT8 name[8];
INT8 age;
INT8 sex;
INT8 class;
INT8 subject;
float score;
} student_score_t;
因为每位学生都有多科学习成绩,故如上结构将占用较大空间。应如下改进(分为两个结构),总的存贮空间将变小,操作也变得更方便。
typedef struct _student_t
{
INT8 name[8];
INT8 age;
INT8 sex;
INT8 class;
} student_t;
第 45 页 共 58 页
HX/QI/012001
typedef struct _student_score_t {
INT16 studentIndex;
INT8 subject;
float score;
} student_score_t;
, 规则13-3:循环体内工作量最小化。
说明:应仔细考虑循环体内的语句是否可以放在循环体之外,使循环体内
工作量最小,从而提高程序的时间效率。
示例:如下代码效率不高。
for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
backSum = sum; /* backup sum */ }
语句“backSum = sum;”完全可以放在for语句之后,如下。 for (ind = 0; ind < MAX_ADD_NUMBER; ind++)
{
sum += ind;
}
backSum = sum; /* backup sum */ , 规则13-4:在多重循环中,应将最忙的循环放在最内层。 说明:减少CPU切入循环层的次数。
示例:
如下代码效率不高。
for (row = 0; row < 100; row++) {
for (col = 0; col < 5; col++)
{
sum += array[row][col];
}
}
可以改为如下方式,以提高效率。
for (col = 0; col < 5; col++) {
for (row = 0; row < 100; row++)
{
第 46 页 共 58 页
HX/QI/012001
sum += array[row][col];
}
}
13.2 建议
, 建议13-1:编程时要经常注意代码的效率。
说明:代码效率分为全局效率、局部效率、时间效率及空间效率。全局效率是站在整个系统的角度上的系统效率;局部效率是站在模块或函数角度上的效率;时间效率是程序处理输入任务所需的时间长短;空间效率是程序所需内存空间,如机器代码空间大小、数据空间大小、栈空间大小等。 , 建议13-2:尽量避免大块内存的memcpy,因为内存COPY效率较低,同
时更重要的是memcpy不检查变量的类型,容易出现内存变量越界问题,
在编程过程中对于结构类型变量最好直接赋值。
-3:在保证软件系统的正确性、稳定性、可读性及可测性的前提, 建议13
下,提高代码效率。
说明:不能一味地追求代码效率,而对软件的正确性、稳定性、可读性及可测性造成影响。
, 建议13-4:仔细分析有关算法,并进行优化。
, 建议13-5:仔细考查、分析系统及模块处理输入(如事务、消息等)的
方式,并加以改进。
, 建议13-6:对模块中函数的划分及组织方式进行分析、优化,改进模块
中函数的组织结构,提高程序效率。
说明:软件系统的效率主要与算法、处理任务方式、系统功能及函数结构有很大关系,仅在代码上下功夫一般不能解决根本问题。
, 建议13-7:编程时,要随时留心代码效率;优化代码时,要考虑周全。 , 建议13-8:不应花过多的时间拼命地提高调用不很频繁的函数代码效
率。
说明:对代码优化可提高效率,但若考虑不周很有可能引起严重后果。 , 建议13-9:要仔细地构造或直接用汇编编写调用频繁或性能要求极高的
函数。
第 47 页 共 58 页
HX/QI/012001
说明:只有对编译系统产生机器码的方式以及硬件系统较为熟悉时,才可使用汇编嵌入方式。嵌入汇编可提高时间及空间效率,但也存在一定风险。 , 建议13-10:在保证程序质量的前提下,通过压缩代码量、去掉不必要
代码以及减少不必要的局部和全局变量,来提高空间效率。 说明:这种方式对提高空间效率可起到一定作用,但往往不能解决根本问题。
, 建议13-11:尽量减少循环嵌套层次。
, 建议13-12:避免循环体内含判断语句,应将循环语句置于判断语句的
代码块之中。
说明:目的是减少判断次数。循环体中的判断语句是否可以移到循环体外,要视程序的具体情况而言,一般情况,与循环变量无关的判断语句可以移到循环体外,而有关的则不可以。
示例:
如下代码效率稍低。
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{
if (dataType == RECT_AREA)
{
areaSum += rectArea[ind];
}
else
{
rectLenSum += rect[ind].len;
rectWidthSum += rect[ind].width;
}
}
因为判断语句与循环变量无关,故可如下改进,以减少判断次数。
if (dataType == RECT_AREA)
{
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
{
areaSum += rectArea[ind];
}
}
else
{
for (ind = 0; ind < MAX_RECT_NUMBER; ind++)
第 48 页 共 58 页
HX/QI/012001
{
rectLenSum += rect[ind].len;
rectWidthSum += rect[ind].width;
}
}
, 建议13-13:尽量用乘法或其它方法代替除法,特别是浮点运算中的除
法。
说明:浮点运算除法要占用较多CPU资源。
示例:
如下表达式运算可能要占较多CPU资源。
#define PAI 3.1416
radius = circleLen / (2 * PAI);
应如下把浮点除法改为浮点乘法。
#define PAI_RECIPROCAL (1 / 3.1416 ) // 编译器编译时,
将生成具体浮点数
radius = circleLen * PAI_RECIPROCAL / 2;
14 内存操作
14.1 规则
, 规则14-1:必须了解编译系统的内存分配方式,尤其时编译系统对不同
类型的变量的内存分配规则,比如局部变量在何处分配,静态变量在何
处分配。
, 规则14-2:内存的大小是一定的,因此各个模块应该尽量减少代码对内
存的占有量。
, 建议14-1:为了保证可靠性,在通信程序中,一般不使用动态申请内存。 , 规则14-3:如果必须使用动态内存,则应该注意:
(1)申请时必须检测申请是否成功;
(2)使用完后,必须释放,否则在异常退出的情况下容易造成系统的崩溃。
(3)必须防止使用已经释放的内存,因此最好在内存释放后,将内存指针或者句柄置成OS规定的无效值,NULLL,0。
第 49 页 共 58 页
HX/QI/012001
, 规则14-4:只引用属于自己的存贮空间。
说明:若模块封装的较好,那么一般不会发生非法引用他人的空间。
, 规则14-5:防止引用已经释放的内存空间。
说明:在实际编程过程中,稍不留心就会出现在一个模块中释放了某个内
存块(如C语言指针),而另一模块在随后的某个时刻又使用了它。要防止这
种情况发生。
, 规则14-6:过程/函数中分配的内存,在过程/函数退出之前要释放。
, 规则14-7:过程/函数中申请的(为打开文件而使用的)文件句柄,在
过程/函数退出之前要关闭。
说明:分配的内存不释放以及文件句柄不关闭,是较常见的错误,而且稍
不注意就有可能发生。这类错误往往会引起很严重后果,且难以定位。
示例:
如下函数在退出之前,没有把分配的内存释放。
INT32 exampleFun ( INT8 gtLen, INT8 *pGtCode )
{
INT8 *pGtBuf;
pGtBuf = (INT8 *) Malloc (MAX_GT_LENGTH);
... //program code, include check pGtBuf if or not
NULL.
/* global title length error */
if (gtLen > MAX_GT_LENGTH)
{
return GT_LENGTH_ERROR; // 忘了释放pGtBuf
}
... // other program code
}
应改为如下。
INT32 exampleFun ( INT8 bGtLen, INT8 *bpGtCode )
{
INT8 *pGtBuf;
pGtBuf = (INT8 * ) malloc ( MAX_GT_LENGTH );
... // program code, include check pGtBuf if or not
第 50 页 共 58 页
HX/QI/012001
NULL.
/* global title length error */
if (gtLen > MAX_GT_LENGTH)
{
free( pGtBuf ); // 退出之前释放bpGtBuf
return GT_LENGTH_ERROR;
}
... // other program code }
14.2 建议
, 建议14-1:在往一个内存区连续赋值之前(memset,memcpy„),应确保内存区的大小能够容纳所赋的数据。如下面的程序是错误的。这种错误容易出现在消息来自于物理层的时候,因为在某些异常情况下,物理层上来的消息可能是无效的但很长,程序中若不加防范,内存区就会溢出,而这种错误往往是一个月或两个月出现一次,这对于需要长时间可靠运行的通信软件来说是致命的。
/* #define MAX_MSG_NUM 100 */ /* #define BUF_SIZE 50 */ BYTE aaMsgBuf[MAX_MSG_NUM][BUF_SIZE];
memcpy(aaMsgBuf[iMsg], pMsgFromBTS, wMsgFromBTSLength);
/* wMsgFromBTSLength = 80 */
, 建议14-2:为防止堆栈的溢出,在函数体内的局部变量占用内存不应过多,例如下面的代码是不合适的:
/* #define MAX_MSG_NUM 100 */ /* #define BUF_SIZE 50 */ BYTE FunctionName(void)
{
BYTE aaMsgBuf[MAX_MSG_NUM][BUF_SIZE];
}
说明:aaMsgBuf应采用别的使用方法或定义成静态变量static。
, 建议14-3:防止内存操作越界。
说明:内存操作主要是指对数组、指针、内存地址等的操作。内存操作越
界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时
第 51 页 共 58 页
HX/QI/012001
一定要仔细小心。
示例:
假设某软件系统最多可由10个用户同时使用,用户号为1-10,那么如
下程序存在问题。
#define MAX_USR_NUM 10
INT8 usrLoginFlg[MAX_USR_NUM]= "";
void setUsrLoginFlg ( INT8 usrNo ) {
if (!usrLoginFlg[usrNo])
{
usrLoginFlg[usrNo]= TRUE;
}
}
当usrNo为10时,将使用usrLoginFlg越界。可采用如下方式解
决。
void setUsrLoginFlg ( INT8 usrNo ) {
if (!usrLoginFlgProc[usrNo - 1])
{
usrLoginFlg[usrNo - 1]= TRUE;
}
}
15 宏
, 规则15-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))
, 规则15-2:将宏所定义的多条表达式放在大括号中。
示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for语
句的书写稍不符规范。
#define INIT_RECT_VALUE( a, b )\
a = 0;\
第 52 页 共 58 页
HX/QI/012001
b = 0;
for (index = 0; index < RECT_TOTAL_NUM; index++)
INIT_RECT_VALUE( rect.a, rect.b );
正确的用法应为:
#define INIT_RECT_VALUE( a, b )\ {\
a = 0;\
b = 0;\
}
for (index = 0; index < RECT_TOTAL_NUM; index++)
{
INIT_RECT_VALUE( rect[index].a, rect[index].b );
}
, 规则15-3:使用宏时,不允许参数发生变化。
示例:
如下用法可能导致错误。
#define SQUARE( a ) ((a) * (a))
INT32 a = 5;
INT32 b;
b = SQUARE( a++ ); // 结果:a = 7,即执行了两次增1。
正确的用法是:
b = SQUARE( a );
a++; // 结果:a = 6,即只执行了一次增1。
16 其它
16.1 规则
, 规则16-1:常量全用大写的字母,用下划线分割单词。
示例:
#define MAX 100
#define MAX_LENGTH 100
, 规则16-2:消息采用大写的字母,用下划线分割单词。具体形式为:模
第 53 页 共 58 页
HX/QI/012001
块名,有意义的消息名,消息类型。
示例:MAP_UPDATE_ACK(MAP:模块名 UPDATE:消息名 ACK:消息类型) , 规则16-3:除非必要,不要用数字或较奇怪的字符来定义标识符。 示例:
如下命名,使人产生疑惑。
#define _EXAMPLE_0_TEST_
#define _EXAMPLE_1_TEST_
void setSls00( INT8 sls );
应改为有意义的单词命名
#define _EXAMPLE_UNIT_TEST_ #define _EXAMPLE_ASSERT_TEST_ void setUdtMsgSls( INT8 sls ); , 规则16-4:当声明用于分布式环境或不同CPU间通信环境的数据结构时,必须考虑机器的字节顺序、使用的位域及字节对齐等问题 。 说明:比如Intel CPU与68360 CPU,在处理位域及整数时,其在内存存
放的“顺序”正好相反。
示例:
假如有如下短整数及结构。
UINT16 exam;
typedef struct _exam_bit_t { /* Intel 68360 */
UINT32 A1: 1; /* bit 0 7 */
UINT32 A2: 1; /* bit 1 6 */
UINT32 A3: 1; /* bit 2 5 */ } exam_bit_t;
如下是Intel CPU生成短整数及位域的方式。
内存: 0 1 2 ... (从低到高,以字节为单位) exam exam低字节 exam高字节
内存: 0 bit 1 bit 2 bit ... (字节的各“位”) exam_bit_t A1 A2 A3
如下是68360 CPU生成短整数及位域的方式。
第 54 页 共 58 页
HX/QI/012001
内存: 0 1 2 ... (从低到高,以字节为单位)
exam exam高字节 exam低字节
内存: 7 bit 6 bit 5 bit ... (字节的各“位”)
exam_bit_t 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 | |
------- ------- ------- --------
| ....
, 规则16-5:系统运行之初,要初始化有关变量及运行环境,防止未经初
始化的变量被引用。
, 规则16-6:系统运行之初,要对加载到系统中的数据进行一致性检查。
说明:使用不一致的数据,容易使系统进入混乱状态和不可知状态。 16.2 建议
, 建议16-1:在同一软件产品内,应规划好接口部分标识符(变量、结构、
函数及常量)的命名,防止编译、链接时产生冲突。
说明:对接口部分的标识符应该有更严格限制,防止冲突。如可规定接口部分的变量与常量之前加上“模块”标识等。
, 建议16-2:除了编译开关/头文件及结构体/联合体/枚举定义等特殊应
用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。
第 55 页 共 58 页
HX/QI/012001
, 建议16-3:定义数据结构时,可以指出参考的协议。
示例:
typedef struct _drx_info_t
{
(((((((
} drx_info_t; /*see TS 24.008----10.5.5.6*/
, 建议16-4:在同一项目组或产品组内,要有一套统一的为集成测试与系
统联调准备的调测开关及相应打印函数,并且要有详细的说明。 说明:本规则是针对项目组或产品组的。
, 建议16-5:在同一项目组或产品组内,调测打印出的信息串的格式要有
统一的形式。信息串中至少要有所在模块名(或源文件名)及行号。 说明:统一的调测信息格式便于集成测试。
, 建议16-6:编程的同时要为单元测试选择恰当的测试点,并仔细构造测
试代码、测试用例,同时给出明确的注释说明。测试代码部分应作为(模
块中的)一个子模块,以方便测试代码在模块中的安装与拆卸(通过调
测开关)。
说明:为单元测试而准备。
, 建议16-7:不使用与硬件或操作系统关系很大的语句,而使用建议的标
准语句,以提高软件的可移植性和可重用性。
, 建议16-8:为用户提供良好的接口界面,使用户能较充分地了解系统内
部运行状态及有关系统出错情况。
, 建议16-9:系统应具有一定的容错能力,对一些错误事件(如用户误操
作等)能进行自动补救。
, 建议16-10:使用代码检查工具(如C语言用PC-Lint)对源程序检查。
(目前我们未提供pc-lint软件)。
, 建议16-11:使用软件工具(如LogiSCOPE)进行代码审查。(目前我们
未提供LogiSCOPE软件)。
17 修改代码注意事项
修改代码原则上不改变代码大的框架及逻辑结构,如需调整框架或逻辑结
第 56 页 共 58 页
HX/QI/012001
构,须与模块责任人或子系统负责人讨论,评估代码调整后对性能的提升以及对当前系统的影响,根据实际情况确认修改与否。
修改他人代码时,如果是大的改动应该遵循整个子系统的代码风格,小的改动应该遵循当前模块的代码风格,最大可能的保证代码风格的一致性,以便维护。
不论是修改自己代码还是修改他人代码,大的修改需要在模块文件头做出说明,小的修改需要在函数说明中说明且在修改处添加修改标记及相应注释。
对于修改代码时不明确的地方需要向相关责任人也就是模块文件头标明的编写者确认,避免修改造成逻辑上的错误。
对于子系统公共模块代码的修改需要对整个子系统进行测试验证方可提交代码,对于其他模块内部代码的修改需要测试验证该模块功能后方可提交代码。
18 结语
编程规范是死的,人是活的,从实际出发,在保证程序的结构清晰,风
格良好,维护方便,性能稳定的基础上,不必太过拘泥于细节。
在编码的时候学会换位思考,不光要有开发者的认真,更要体味测试者
维护者的如履薄冰,使用者的天马行空,结合规范从产品的角度考的严谨,
虑自己的编码,使规范成为一种习惯,尽最大可能减少因代码风格或编码习
惯造成的一系列不必要的工作量。
大家在编码规范上有什么异议或好的建议欢迎提出,此规范可进一步修改完善,以期最大程度的服务整个开发过程。
第 57 页 共 58 页