软件编程规范 软件编程规范
北京和利时系统工程股份有限公司
技 术 文 件
文件名称:秦山一期KIT.PROC.软件编程规范
文件编号:
项目名称:秦山一期电站计算机系统改造软件
项目编号:0316D
物料编码:
受控标识:
第 1 册
共 11 页
共 1 册
版本
状态
拟制
审核
批准
批准日期
修订信息
A
牛玉贞
...
软件编程规范
北京和利时系统工程股份有限公司
技 术 文 件
文件名称:秦山一期KIT.PROC.软件编程规范
文件编号:
项目名称:秦山一期电站计算机系统改造软件
项目编号:0316D
物料编码:
受控标识:
第 1 册
共 11 页
共 1 册
版本
状态
拟制
审核
批准
批准日期
修订信息
A
牛玉贞
相关部门会签:
文件发放
部门
主送
阅知
部门
主送
阅知
软件开发部
□
□
ERP事业部
□
□
硬件开发部
□
□
交通信息系统事业部
□
□
技术管理部
□
□
国际贸易部
□
□
系统开发部
□
□
质量部
□
□
核电项目部
□
□
财务部
□
□
生产管理部
□
□
证券部
□
□
采购供应部
□
□
办公室
□
□
营销部
□
□
人力资源部
□
□
工业自动化事业部
□
□
信息管理部
□
□
系统集成事业部
□
□
后勤管理部
□
□
31.
引言
31.1
目的
31.2
范围
32.
规范
32.1
文件
72.2
版面风格
122.3
标识符命名
142.4
与宏
172.5
代码的可靠性
1. 引言
1.1 目的
本规范的目的在于增加源代码的可读性,减少程序员对代码理解上的偏差,使程序员能够编写出可靠的代码,降低代码维护成本。
1.2 范围
本规范内容涉及范围包括:文件、版面、注释、标识符、变量和结构、函数、宏以及可理解性等。本规范适用于公司开发的所有软件产品。在新软件的编码过程中本规范必须执行。
2. 规范
2.1 文件
2.1.1 头文件的名称一律为小写,格式为“子系统名_文件名.h”。例如:ipf_protocol.h等。
2.1.2 头文件的格式如下:
· 注释头,格式参见下面的例子;
· 头文件预编译开关开始,格式为:
#ifndef 预编译开关
#define 预编译开关
其中预编译开关格式为:“ _文件名_H”,其中文件名一律大写
· 头文件内容;
· 头文件预编译开关结束,格式为:
#endif
用来和头文件预编译开关的开始对应。
例如:以下为ipf_ip.h头文件的内容:
/************************************************************
Copyright (c) Beijing Hollysys Co.,Ltd
ALL RIGHTS RESERVED
Description: // 用于详细说明此程序文件完成的主要功能
*************************************************************/
#ifndef _IPF_IP_H
#define _IPF_IP_H
...
<头文件正文>
...
#endif
2.1.3 头文件的定义要有层次,禁止交叉引用。
说明:头文件的层次设置为公共模块、私有模块。头文件的引用次序为下层头文件引用上层头文件、私有头文件引用公共头文件,声明结构时尤其要注意,不允许出现交叉引用的情况。
示例:如下定义不符合规范
头文件isdn_a.h
/************************************************************
Copyright (c) Beijing Hollysys Co.,Ltd
ALL RIGHTS RESERVED
Description: 定义配置数据结构
************************************************************/
#ifndef _ISDN_A_H
#define _ISDN_A_H
#include “isdn_b.h”
typedef struct
{
...;
ISDN_CONTROL stIsdnControl;
...;
}ISDN_ CONFIG;
#endif
头文件isdn_b.h
/************************************************************
Copyright (c) Beijing Hollysys Co.,Ltd
ALL RIGHTS RESERVED
Description: 定义控制数据结构
*************************************************************/
#ifndef _ISDN_B_H
#define _ISDN_B_H
#include “isdn_a.h”
typedef struct
{
...;
ISDN_CONFIG stIsdnConfig;
...;
}ISDN_CONTROL;
#endif
为了解决上述矛盾,可以将两个结构合并到一个文件中声明。
2.1.4 文件中如果引用系统头文件,必须使用“<“和“>“;如果引用自定义的头文件,必须使用“”“和“”“。
说明:系统头文件是指由编译系统提供的头文件。
示例:如下书写不符合规范。
#include “stdlib.h”
#include
应该改作:
#include
#include “isdn_config.h”
2.1.5 头文件中只能声明变量类型,禁止定义变量。
说明:如果在头文件中定义变量,当有多个源文件引用该头文件时,会出现重复定义的错误。
示例:如下头文件是不规范的。
头文件isdn_a.h
/************************************************************
Copyright (c) Beijing Hollysys Co.,Ltd
ALL RIGHTS RESERVED
Description: 定义配置数据结构
*************************************************************/
#ifndef _ISDN_A_H
#define _ISDN_A_H
typedef struct
{
...;
}ISDN_ CONFIG;
ISDN_CONFIG g_stIsdnConfig;
#endif
结构变量g_stIsdnConfig不能在头文件中定义,只能在源文件中定义。可以在相应的源文件定义后,头文件作如下改动:
ISDN_CONFIG g_stIsdnConfig;改为
extern ISDN_CONFIG g_stIsdnConfig;
2.1.6 头文件的声明顺序,应该有层次感。
说明:头文件的声明顺序一般是宏、结构、函数、变量。函数在头文件中声明时开头可以不加“extern”
2.1.7 源文件名称一律小写,格式为:子系统名_文件名.c,
示例:ipf_ pkt.c表示在IPF子系统的包处理文件。
2.1.8 源文件必须加注释头。
源文件注释头格式为:
/************************************************************
Copyright (c) Beijing Hollysys Co.,Ltd
ALL RIGHTS RESERVED
Description: // 用于详细说明此程序文件完成的主要功能
*************************************************************/
2.2 版面风格
2.2.1 程序块采用缩进风格编写,缩进规定为4个空格(建议不使用TAB符号,因为各个系统TAB符号的空格数不一样)。变量说明之后必须加空行。
说明:由开发工具自动生成的代码例外。
示例:如下例子不符合规范。
void isdn_InitConfig(void)
{
WORD wConfigNumner;
...; //program code
}
应如下书写
void isdn_InitConfig(void)
{
WORD wConfigNumner;
...; //program code
}
2.2.2 不允许把多个短语句写在一行中,一行只写一条语句。
示例:如下例子不符合规范。
wLength = wWidth = 0;
或wLength = 0, wWidth = 0;
应如下书写
wLength = 0;
wWidth = 0;
禁止如下写法:
int i = j = 0;
或int i = 0,j = 0;
应该写作:
int i = 0;
int j = 0;
或
int i,j;
i = 0;
j = 0;
2.2.3 do、while、switch、case、default、if、else、for等语句自占一行,且if、else语句在同一列。
示例1:如下例子不符合规范。
do {
...; //program code
} while (pstUser != NULL);
应如下书写:
do
{
...; //program code
} while (pstUser != NULL);
示例2:如下例子不符合规范。
while (pstUser != NULL) {
...; //program code
}
应如下书写:
while (pstUser != NULL)
{
...; //program code
}
示例3:如下例子不符合规范。
switch (dwCounter) {
case 1: dwCounter++;
...; //program code
break;
default:break;
}
应如下书写:
switch (dwCounter)
{
case 1:
dwCounter++;
...; //program code
break;
default:
break;
}
示例4:如下例子不符合规范。
if (pstUser == NULL) return;
else {
...; //program code
}
应如下书写:
if (pstUser == NULL)
{
return;
}
else
{
...; //program code
}
示例5:如下例子不符合规范。
for (i = 0;i < 10;i++) {
...; //program code
}
应如下书写:
for (i = 0;i < 10;i++)
{
...; //program code
}
2.2.4 函数的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格。
示例:如下例子不符合规范。
int isdn_Config(void)
{
...; // program code
return 0;
}
应该改为
int isdn_Config(void)
{
...; // program code
return 0;
}
示例:如下例子不符合规范。
typedef struct
{
WORD wSlot;
WORD wPort;
}ISDN_CONFIG;
应该改为
typedef struct
{
WORD wSlot;
WORD wPort;
}ISDN_CONFIG;
2.2.5 在switch的处理程序块中,case和default语句下的处理语句也要遵从语句缩进要求。
示例:
以下写法是不规范的
switch (wEvent)
{
case 1:
...; //program code
break;
}
应该改为:
switch (wEvent)
{
case 1:
...; //program code
break;
default:
break;
}
2.2.6 程序块的分界符(大括号'{'和'}')应各独占一行并且位于同一列,同时与引用它们的语句左对齐。
示例:如下例子不符合规范。
if (...)
{
... // program code
}
void isdn_ExampleFun( void ) {
... // program code
}
应如下书写。
if (...)
{
... // program code
}
void isdn_ExampleFun( void )
{
...; // program code
}
2.2.7 函数头部应进行注释,列出:函数的名称、功能、输入参数、输出参数、返回值等。
示例:编写函数时候按照下面这段注释编写。
/*************************************************
Func Name: // 函数名称
Description: // 函数功能的描述
Input: // 输入参数说明
Output: // 对输出参数和函数返回值的说明
*************************************************/
2.2.8 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面。
示例:如下例子不符合规范。
isdn_Init();
//初始化ISDN任务
应如下书写:
//初始化ISDN任务
isdn_Init();
2.2.9 对有实际含义的变量或者常量的注释,应放在其上方相邻位置或右方。对宏的注释,应放在上面,不可放在其右方或下方。对数据结构的声明(包括数组、结构、类、枚举等),注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。
示例:
以下写法不规范
#define VOS_ERROR_MSG_ON //错误信息打印输出开关
应改为
// 错误信息打印输出开关
#define VOS_ERROR_MSG_ON
2.2.10 允许使用”//”进行注释
示例:以下注释是允许的
// 根据CFM所存的配置信息更新接口配置信息
void isdn_SetConfigData(void)
{
...; //program code
}
2.3 标识符命名
2.3.1 局部变量采用大小写混排的匈牙利方式命名,命名格式为前缀+变量名称,其中变量名由一个或一个以上的组成,每个单词首字母大写,其余一律小写。
说明:前缀要表明变量类型。下表为定义变量时候使用的前缀。
类型
前缀
类型名称
BOOLEAN
b
布尔型
BYTE
by
无符号字符型
WORD
w
无符号短整型
DWORD
dw
无符号长整型
char
c
字符型
short
s
短整型
int
i
整型
long
l
长整型
st
结构类型
un
联合类型
p
指针
pby
BYTE的指针
pw
WORD的指针
pdw
DWORD的指针
pst
结构的指针
pun
联合的指针
pa
数组的指针
pfn
函数指针
pm
消息的指针
pc
字符型的指针
ps
短整型的指针
pi
整型的指针
pl
长整型的指针
pp(根据类型添加其他前缀)
指针的指针
a
数组
aby
BYTE数组
aw
WORD数组
adw
DWORD数组
ast
结构数组
aun
联合数组
afn
函数数组
ac
字符型数组
as
短整型数组
ai
整型数组
al
长整型数组
sz
以null结尾的字符串型
示例: 以下的书写不规范
WORD slot;
BYTE *byAccess;
DWORD arp_frame_count;
应该写作:
WORD wSlot;
BYTE *pbyAccess;
DWORD dwArpFrameCount;
2.3.2 普通宏的定义使用全大写字母加下划线的方式,结构为:子系统+下划线+宏内容名称。
示例:
#define IP_MAX_HWA_LEN 6
#define IP_VER(x) ((x >>4) & 0xF0)
2.3.3 函数的命名为:前缀+下划线+字符串。其中前缀为全小写的子系统名称,字符串由一个或多个单词组成,每个单词首字母大写,其他字母小写。
示例:
void vos_MsgCreate(void);
void icmp_PktRecv(void);
2.3.4 调试用的编译开关是开头为下划线的全大写字符串,具体格式是:下划线+DEBUG+子系统名称+下划线+名称。
示例:_DEBUG_IPF_IP_PACKET、_DEBUG_TCP_OPTION等。
2.3.5 结构联合类型命名规则为:子系统名称+下划线+结构名称、子系统名称+下划线+联合名称,其中所有字母大写。
示例:
typedef struct
{
...;
}IP_TRACEROUTE;
typedef union
{
...;
}ICMP_PKT;
2.3.6 全局变量命名的具体格式是:前缀+下划线+子系统名+下划线+字符串,其中前缀为小写的“g”,子系统名称全部小写,字符串由一个或多个单词组成,每个单词首字母大写,其他字母小写。
示例:g_icmp_TraceRouteQueue、g_ospf_InterfList等。
2.3.7 消息宏定义采用以下格式:前缀+下划线+字符串+下划线+后缀。其中前缀采用小写的“mm”,字符串标记消息宏的具体含义,由一个或多个单词组成,每个单词首字母大写,其他字母小写,后缀为XXXtoYYY,表示消息的传送方向,XXX表示源子系统名,YYY为目的子系统名,全部用大写字母表示。
示例:mm_AddRoute_BGPtoRTMGT表示从BGP协议发送到路由管理实体的增加一条路由的消息,mm_ShowIpRoute_CLItoRTMGT表示从CLI发送到路由管理子系统的要求显示路由表的消息。
2.4 函数与宏
2.4.1 对函数的返回值要仔细、全面地处理。
说明:对提供返回值的函数,尤其是接口函数,其返回值必须检查。
示例:以下函数是不规范的。
void isdn_SendMessage(void)
{
...; //program code
vos_MsgPost(...); // 发送消息
}
应该改作
void isdn_SendMessage(void)
{
INT32 iPost;
...; //program code
iPost = vos_MsgPost(...); // 发送消息
if (iPost == VOS_OK)
{
...; //处理代码
}
else
{
...; //处理代码
}
}
2.4.2 接口函数的输入参数(尤其是指针和数组下标)、非输入参数的合法性必须检查。
说明:函数的输入主要有两种:一种是参数输入;另一种是非参数输入,包括全局变量、数据文件等,这些参数都需要作检查。
示例:下面函数的实现不符合规范。
RESULT isdn_Receive(NI_CELL *pstCell,SK_BUF *pstSkBuf)
{
...; //接收数据代码
return OK;
}
应改作以下写法。
RESULT isdn_Receive(NI_CELL *pstCell,SK_BUF *pstSkBuf)
{
if ((pstCell == NULL) || (pstSkBuf == NULL))
{
return FALSE;
}
...; //接收数据代码
return OK;
}
2.4.3 禁止把函数的参数作为工作变量
说明:需要做改变的参数,应该先用局部变量代之,最后再将该局部变量的内容赋给该参数。
示例:下面函数的实现不符合规范。
BOOLEAN isdn_SumData(WORD wNumber,WORD *pwData, WORD *pwSum)
{
WORD wCount;
if ((pwData == NULL) || (pwSum == NULL))
{
return FALSE;
}
*pwSum = 0;
for (wCount = 0; wCount < wNumber; wCount++)
{
*pwSum += pwData[wCount]; // pwSum成了工作变量,不规范
}
return TRUE;
}
应改作以下写法。
BOOLEAN isdn_SumData(WORD wNumber,WORD *pwData, WORD *pwSum)
{
WORD wCount;
WORD wSumTemp;
if ((pwData == NULL) || (pwSum == NULL))
{
return FALSE;
}
wSumTemp = 0;
for (wCount = 0; wCount < wNumber; wCount++)
{
wSumTemp += pwData[wCount];
}
*pwSum = wSumTemp;
return TRUE;
}
2.4.4 明确函数的返回值,当函数不需要返回值时要定义为void。
示例:如下函数写作不规范
isdn_InitCtrlBlock(void)
{
...; //program code
}
应改作:
void isdn_InitCtrlBlock(void)
{
...; //program code
}
2.4.5 用宏定义表达式时,要使用完备的括号。
示例:如下定义的宏都存在一定的风险,不符合规范。
#define MAX(a,b) a > b ? a : b
#define MAX(a,b) (a > b ? a : b)
#define MAX(a,b) (a) > (b) ? (a) : (b)
正确的定义应为:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
2.4.6 使用宏时,不允许参数发生变化。
示例:如下用法导致错误。
#define MAX(a, b) ((a) > (b) ? (a) : (b))
iResult = MAX(iRx++,iTx);
将被预处理器解释为
iResult = ((iRx++) > (iTx) ? (iRx++) : (iTx));
此时如果输入
iRx = 6;
iTx = 5;
代码执行结果iResult = 7,此时iRx为8。
应该改作如下用法:
iResult = MAX(iRx,iTx);
iRx++;
2.5 代码的可靠性
2.5.1 系统运行之初,要初始化所有本系统的全局变量,禁止未经初始化的全局变量被引用。
说明:使用未初始化的数据,容易使系统进入混乱状态。
示例:以下的全局变量如果没有在系统运行时初始化,在被函数使用时会出错
ISDN_CELL *g_pstIsdnCell[ISDN_MAX_CELL_NUM];
void isdn_ShowChannel(WORD wSlot,WORD wPort)
{
ISDN_CELL *pstIsdnCell = 0;
int i;
for (i = 0; i < ISDN_MAX_CELL_NUM;i++)
{
if (g_stpIsdnCell[i] != NULL)
{
pstIsdnCell = g_pstIsdnCell[i];
// 如果g_pstIsdnCell没有初始化为空,以下语句会出错
if ((pstIsdnCell->wSlot == wSlot) \
&& (pstIsdnCell->wPort == wPort))
break;
}
}
...; //other program code
}
2.5.2 申请内存之后,应该立即检查指针值是否为NULL?(防止使用指针值为NULL的内存)
说明:申请内存时,如果申请失败,应该避免使用该指针。必须使用
if (p == NULL) 或if (p != NULL)进行防错处理。
2.5.3 禁止将未被初始化的内存作为零值使用。
说明:创建的数组或动态申请的内存,其初始值是不确定的,不能当作零处理。
示例:以下代码是不规范的。
void isdn_CheckIe(BYTE *pbyIe,WORD wIeLen)
{
WORD wTempLen;
//wTempLen未初始化,其初始值不一定为0
while(wTempLen < wIeLen)
{
...; //other program code
wTempLen += 2;
}
}
应作如下改写:
void isdn_CheckIe(BYTE *pbyIe,WORD wIeLen)
{
WORD wTempLen;
wTempLen = 0;
while (wTempLen < wIeLen)
{
...; //other program code
wTempLen += 2;
}
}
2.5.4 动态内存的申请与释放要配对,防止内存泄漏。
说明:内存泄漏问要高度重视。内存泄漏会引起系统死机或崩溃,一般在系统启动很长一段时间后才发作,不易察觉,一般的测试手段也不到。
2.5.5 在switch的处理程序块中,必须有default语句;并且每个case语句都要有对应的break。
说明:如果一个case下的处理语句需要调用下一个case的处理语句,可以将下一个case的处理语句copy过来。
示例:以下写法是不规范的
switch (wEvent)
{
case 1:
isdn_SendEvent(wEvent);
case 2:
wEvent++;
break;
default:
break;
}
应该改为:
switch (wEvent)
{
case 1:
isdn_SendEvent(wEvent);
wEvent++;
break;
case 2:
wEvent++;
break;
default:
break;
}
2.5.6 不要滥用goto语句。
说明:goto语句会破坏程序的结构性,除非确实需要,不要使用goto语句。
2.5.7 使用显式的数据类型转换,避免让编译器进行隐式的数据类型转换。
说明:使用显示的数据转换一方面增加可读性,有利于代码的维护;另一方面避免隐式的数据类型转换引起的编译告警。
示例:
以下的书写不符合规范
NI_CELL *isdn_Load(void)
{
NI_CELL *pstNiCell;
pstNiCell = vos_malloc(sizeof(NI_CELL));
...; //program code
return pstNiCell;
}
应改作:
NI_CELL *isdn_Load(void)
{
NI_CELL *pstNiCell;
pstNiCell = (NI_CELL*)vos_malloc(sizeof(NI_CELL));
...; //program code
return pstNiCell;
}
2.5.8 不要使用难懂的技巧性很高的语句。
说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。
示例:如下表达式,考虑不周就可能出问题,也较难理解。
*pbyState ++ += 1;
* ++ pbyState += 1;
应分别改为如下。
*pbyState += 1;
pbyState ++; // 此二语句功能相当于" * pbyState ++ += 1; "
++ pbyState;
*pbyState += 1; // 此二语句功能相当于" * ++ pbyState += 1; "
第 17 页 共 22页
_992860125.doc
本文档为【软件编程规范】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。