1 编辑器 (3)
2 程序文件的结构 (3)
2.1 说明 (3)
2.2 编译宏定义 (4)
2.3 包含文件 (4)
2.4 用户数据类型定义 (4)
2.5 管脚定义 (5)
2.6 常量 (5)
2.7 变量 (5)
2.8 正文 (6)
2.8.1 正文顺序[推荐] (6)
2.8.2 正文内容 (6)
2.9 长的常量数组 (6)
3 命名 (7)
3.1 常量、编译宏命名 (7)
3.2 变量命名 (7)
3.3 函数命名 (8)
4 程序的版式 (9)
4.1 缩进:统一使用编辑器的TAB键进行缩进,同时设定其缩进量为系统默认 (9)
4.2 空行:相对独立的程序块之间、变量说明之后必须加空行 (10)
4.3 代码行 (11)
4.3.1 一行代码只做一件事情 (11)
4.3.2 if、for、while、do 等语句自占一行 (11)
4.3.3 程序块的分界符独占一行 (12)
4.3.4 多运算符的代码行,不使用默认优先级 (13)
4.3.5 不要使用难懂的技巧性很高的语句 (13)
4.4 空格 (14)
4.4.1 必要时留空格,使代码更清晰[推荐] (14)
4.4.2 二元操作符前后留空格 (14)
4.4.3 一元操作符前后不留空格 (14)
4.5 长行拆分 (15)
4.5.1 较长的语句(>80字符)要分成多行书写 (15)
4.5.2 循环、判断等语句要在低优先级操作符处划分新行 (16)
4.5.3 函数或过程中的参数较长,则要进行适当的划分 (16)
5 全局变量定义时初始化 (16)
6 注释 (17)
6.1 必须加注释的位置[推荐] (17)
6.2 注释应适量且准确[推荐] (17)
6.3 函数接口的注释格式 (17)
6.4 变量的注释格式[推荐] (18)
6.5 注释与所描述内容进行同样的缩排 (19)
6.6 将注释与其上面的代码用空行隔开 (20)
6.7 程序块结束行加注释[推荐] (20)
7 EEPROM数据保护 (21)
8 程序失效复位处理[推荐] (21)
9 检查 (21)
9.1 数组下标检查 (21)
9.2 指针检查 (22)
9.3 易混淆操作符检查 (22)
9.4 右值变量检查 (22)
9.5 差1错误检查 (22)
9.6 变量溢出检查 (23)
9.7 类型转换检查 (23)
1编辑器
程序统一使用VC的编辑器。
2程序文件的结构
如果没有涉及使用平台化的用户文件,通常只能有一个用户主程序文件。
用户程序文件的内容按说明、编译宏定义、包含文件、用户数据类型定义、管脚定义、常量、变量、正文、长的常量数组的顺序编写。
2.1说明
说明:说明位于用户程序文件的开头,主要内容有:
(1)版权信息。
(2)文件名称。
(3)程序说明:说明程序用途、功能及使用方法等。
(4)版本记录。
(5)开发环境。
示例:
//版权信息
//Copyright (c) 2001, 海湾安全技术有限公司产品开发部
// All rights reserved.
// 文件名称:filename.c
// 程序说明:GST5000控制器主程序……
//版本记录:
//V101:
//1、增加警铃输出延时;
//2、修正****Bug。
//作者:***
//时间:****.**.**
//V100:正式发布。
//作者:***
//时间:****.**.**
//编译工具:Keil C 6.0
2.2编译宏定义
说明:同一产品的不同硬件程序和系列产品的程序,应采用编译宏定义,来减少维护的工作量;
2.3包含文件
说明:包含引用的标准库头文件以及用户头文件。用#include
示例:
#include
#include “user_header.h”
2.4用户数据类型定义
说明:定义结构、联合、枚举以及其它非标准的数据类型;
示例:
typedef union MacData //联合
{
unsigned char Buf[16]; //缓冲区
struct //结构
{
unsigned char Mac; //类型
unsigned char Len; //长度
unsigned char Addr; //地址
unsigned char Data[13]; //数据
}Msg;
};
#define uint unsigned int;//自定义数据类型
2.5管脚定义
说明:定义本程序使用的管脚。
2.6常量
说明:
●有特定意义的常数必须进行宏定义;
●同一特定意义的常数如在多个地方应用,必须进行宏定义;
●调试维护需要调整的常数必须进行宏定义;
●无特定意义的循环上、下限,不需进行宏定义。
2.7变量
说明:定义程序所使用的变量。
●有关联的、相同地址空间、初始化、不需初始化的变量要放在一起;
2.8正文
2.8.1正文顺序[推荐]
说明:
●应按主程序和中断程序的共用子程序、各中断相关子程序和中断程序、主程序相
关子程序和主程序;
●关系紧密的子程序应放在一起,并按照从低到高的层次放。尽量取消函数的声明,
可通过注释说明;
●除主程序和中断程序外,在不影响可读性的情况下不应设置只一个地方调用的函
数。
2.8.2正文内容
说明:包含初始化程序、主循环体程序和中断程序。
2.8.2.1初始化程序[推荐]
说明:应按管脚初始化、RAM初始化、内部设置初始化、外设初始化、打开定时器、打开中断的顺序进行初始化。
2.8.2.2主循环程序和中断程序[推荐]
说明:
●定时应采用计时器处理,不能存在长时间的停滞等待;
●不允许多个事件使用一个软件计时器。计时器宜采用倒计时;
●中断事件的响应如果在主循环能够完成响应,应放在主循环。
2.9长的常量数组
说明:在此定义长的常量数组,如字库、图案等。
3命名
命名的风格必须保持一致。
3.1常量、编译宏命名
说明:常量、编译宏全部大写,单词间使用下划线分隔。
示例:
#define MAIN_DELAY_NUM 1
3.2变量命名
说明:通常,在变量中加入前缀以增进人们对程序的理解。其前缀的通用格式为:数据类型缩写+模块名+。同时,为了区分全局和局部变量,也兼顾命名的方便,特采用如下变量命名规则:
●全局变量大小写结合,数据类型缩写后,单词首字母大写;
●局部变量全部小写,数据类型缩写后,使用下划线分隔各单词;局部变量在不影
响可读性的前提下,可以不写数据类型;
●unsigned char类型的缩写不写;
●汇编中,与MPU数据总线(如8位、16位)一致数据类型的缩写不写。
数据类型缩写:
boolean b
char c
long l
float f
double d
int i
unsigned int ui
指针p
结构s
输入管脚bi
输出管脚bo
示例:
//管脚定义
sbit biFire = P1^1;
sbit boFireRelay = P1^2;
//全局变量定义
bit bFault;
unsigned char FireAckNum;
unsigned char DeviceStatus[242];
unsigned char* pDevice;
char cSampleValue;
long lTime;
long* plEntry;
float fRadius;
double dArea;
int iFireRelayDelay;
unsigned int uiFaultRelayDelay;
unsigned int uiSounderDelay[4];
void GetSampleValue(void)
{
unsigned char temp;
bit b_fault;
long l_fire_delay;
}
3.3函数命名
说明:函数命名应当直观且可以拼读,可望文知意,不必进行“解码”。标识符采用英文单词或其组合,便于记忆和阅读,并且每个单词的首字母大写,长单词可以缩写,但缩写要使用大家基本认可的,并且必要是进行注释说明。
下单词的缩写能够被大家基本认可:
temp 可缩写为tmp;
flag 可缩写为flg ;
statistic 可缩写为stat ;
increment 可缩写为inc;
message 可缩写为msg;
4程序的版式
4.1缩进:统一使用编辑器的TAB键进行缩进,同时设定其缩进量为系统默认
说明:程序要采用统一缩进风格编写。在下列情况下,必须采用缩进:函数或过程、类的定义、结构的定义、枚举的定义、判断和循环语句中的程序都要采用如上的缩进方式。但如果同一程序块的缩进级数太多(一般不超过5级),可考虑使用函数减少缩进级数。
示例:
//风格良好的格式
void Function(float x, float y, float z)
{
if (…)
{
…
while (…)
{
…
} // end of while
…
} // end of if
…
}
//风格很差的格式
void Function(float x, float y, float z)
{
if (…)
{
…
while (…)
{
…
} // end of while
…
} // end of if
…
}
4.2空行:相对独立的程序块之间、变量说明之后必须加空行
说明:空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。
示例:
如下例子不符合规范。
if (!IsValidNI (ni))
{
... // program code
}
repssnInd = ssnData[index].repssnIndex;
repssnNI = ssnData[index].NI;
应如下书写
if (!IsValidNI (ni))
{
... // program code
}
repssnInd = ssnData[index].repssnIndex;
repssnNI = ssnData[index].NI;
4.3代码行
4.3.1一行代码只做一件事情
说明:如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。
示例:
(a)为风格良好的代码行
int width; // 宽度
int height; // 高度
int depth; // 深度
x = a + b;
y = c + d;
z = e + f;
(b)为风格不良的代码行。
int width, height, depth; // 宽度高度深度
X =a + b; y = c + d; z = e + f;
4.3.2if、for、while、do 等语句自占一行
说明:执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误示例:
(a)为风格良好的代码行
if (width < height)
{
DoSomeThing();
}
for (initialization; condition; update)
{
DoSomeThing ();
}
(b)为风格不良的代码行。
if (width < height) DoSomeThing ();
Other();
for (initialization; condition; update)
DoSomeThing ();
Other();
4.3.3程序块的分界符独占一行
说明:程序块的分界符(如C/C++语言的大括号‘{’和‘}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。
示例:如下例子不符合规范。
for (...) {
... // program code
}
if (...)
{
... // program code
}
void ExampleFun( void )
{
... // program code
}
应如下书写。
for (...)
{
... // program code
}
if (...)
{
... // program code
}
void ExampleFun ( void )
{
... // program code
}
4.3.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)造成了判断条件出错。
4.3.5不要使用难懂的技巧性很高的语句
说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。
示例:如下表达式,考虑不周就可能出问题,也较难理解。
* statPoi ++ += 1;
* ++ statPoi += 1;
应分别改为如下。
*statPoi += 1;
statPoi++; // 此二语句功能相当于“* statPoi ++ += 1; ”
++ statPoi;
* statPoi += 1; // 此二语句功能相当于“* ++ statPoi += 1; ”
4.4空格
4.4.1必要时留空格,使代码更清晰[推荐]
说明:采用这种松散方式编写代码的目的是使代码更加清晰。如果语句已足够清晰则不需要加空格,如多重括号间不必加空格,像const、virtual、inline、case 等关键字之后,为了突出关键字,至少要留一个空格。像if、for、while 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。
4.4.2二元操作符前后留空格
说明:如“=”、“+=”“>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。
4.4.3一元操作符前后不留空格
说明:一元操作符如“!”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。“[]”、“.”、“->”这类操作符前后不加空格。
示例:
(1) 逗号、分号只在后面加空格。
int a, b, c; //良好的风格
int a,b,c; //不好的风格
(2)比较操作符, 赋值操作符"="、"+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",
位域操作符"<<"、"^"等双目操作符的前后加空格。
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 (a >= b && c > d)
4.5长行拆分
4.5.1较长的语句(>80字符)要分成多行书写
说明:较长的语句(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。
示例:
permCountMsg.head.len = NO7_TO_STAT_PERM_COUNT_LEN
+ STAT_SIZE_PER_FRAM * sizeof( _UL );
actTaskTable[frameId * STAT_TASK_CHECK_NUMBER + index].occupied
= statPoi[index].occupied;
actTaskTable[taskNo].durationTrueOrFalse
= sysGetSccpStatisticState( statItem );
reportOrNotFlag = ((taskNo < MAX_ACT_TASK_NUMBER)
&& (IsStatItemValid (statItem))
&& (actTaskTable[taskNo].ResultData != 0));
4.5.2循环、判断等语句要在低优先级操作符处划分新行
说明:循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首。
示例:
if ((taskNo < maxActTaskNumber)
&& (IsStatItemValid (statItem)))
{
... // program code
}
for (i = 0, j = 0; (i < BufferKeyword[wordIndex]. WordLength)
&& (j < NewKeyword. WordLength); i++, j++)
{
... // program code
}
4.5.3函数或过程中的参数较长,则要进行适当的划分
说明:若函数或过程中的参数较长,则要进行适当的划分。
示例:
StrCompare( (BYTE *) & statObject,
(BYTE *) & (actTaskTable[taskNo].statObject),
sizeof (_STAT_OBJECT));
FlashActDuration( statItem, frameId *STAT_TASK_CHECK_NUMBER
+ index, statObject );
5全局变量定义时初始化
变量定义的同时初始化,未初始化的变量需注释其原因。
6注释
6.1必须加注释的位置[推荐]
说明:注释符有块注释“/*…*/”行注释和“//…”。应使用“//…”进行注释,使用“/*…/*”进行代码屏蔽。注释通常用于:
●用户程序文件开头的说明;
●编译宏定义、结构和联合定义、管脚定义、常量、重要变量;
●重要的代码行或段落提示;
●函数接口说明。
6.2注释应适量且准确[推荐]
说明:虽然注释有助于理解代码,但不必要过多注释,也不可与代码体现的意思不一致。注释是对代码的“提示”,而不是文档,应该“点到为止”。注释不仅要提示代码,还要体现其显示意义。过多的注释,会让人眼花缭乱。如果代码本来就是清楚的,则不必加注释;否则多此一举,令人厌烦。不一致的注释,更让人费解。
应从标识符命名、程序流程上下功夫,让代码本身就能说明自己,进而减少注释。
示例:多余的注释
i++; // i 加1,
6.3函数接口的注释格式
说明:对函数接口进行正确而必要的注释,有助于该函数的使用和维护,甚至有助于在代码级上测试该函数。
示例:函数接口的注释
//函数介绍:
//输入参数:
//输出参数:
//返回值:
//备注:
//过程参数:(汇编中,过程参数也是需要注明的。)
void DrawTriangle(float x, float y, float z)
{
if (…)
{
…
while (…)
{
…
} // end of while
…
} // end of if
…
}
6.4变量的注释格式[推荐]
说明:全局变量要有较详细的注释,其注释的内容包括:
●功能;
●取值范围;
●存取注意事项;
●存取函数或过程;
●其它说明;
示例:变量的注释。
// active statistic task number
#define MAX_ACT_TASK_NUMBER 1000
#define MAX_ACT_TASK_NUMBER 1000 // active statistic task number
示例:可按如下形式说明枚举/数据/联合结构。
// sccp interface with sccp user primitive message name
enum SCCP_USER_PRIMITIVE
{
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 };
示例:全局变量要有较详细的注释。
// 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() // 使用方法
unsigned char g_uchGTTranErrorCode;
6.5注释与所描述内容进行同样的缩排
说明:可使程序排版整齐,并方便注释的阅读与理解。
示例:
//如下例子,排版不整齐,阅读稍感不方便
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
}
6.6将注释与其上面的代码用空行隔开
说明:使人能很清晰的看出,注释与被注释的代码。
示例:如下例子,显得代码过于紧凑。
// code one comments
program code one
// code two comments
program code two
应如下书写
// code one comments
program code one
// code two comments
program code two
6.7程序块结束行加注释[推荐]
说明:当代码段较长,特别是多重嵌套时,在程序块的结束行右方加注释标记,以表明某程序块的结束,这样做可以使代码更清晰,更便于阅读。
示例:参见如下例子。
if (...)
{
// program code