长发短发哪个更有气质:转贴:完全的C51编程规范(转)(上)

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 16:01:36
完全的C51编程规范(转)(上)引言
今天人们越来越明白软件设计更多地是一种工程,而不是一种个人艺术。由于大型产品的开发通常由很多的人协同作战,如果不统一编程规范,最终合到一起的程序,其可读性将较差,这不仅给代码的理解带来障碍,增加维护阶段的工作量,同时不规范的代码隐含错误的可能性也比较大。
BELL实验室的研究资料表明,软件错误中18%左右产生于概要设计阶段,15%左右产生于详细设计阶段,而编码阶段产生的错误占的比例则接近50%;分析表明,编码阶段产生的错误当中,语法错误大概占20%左右,而由于未严格检查软件逻辑导致的错误、函数(模块)之间接口错误及由于代码可理解度低导致优化维护阶段对代码的错误修改引起的错误则占了一半以上。
可见,提高软件质量必须降低编码阶段的错误率。如何有效降低编码阶段的错误呢?BELL实验室的研究人员制定了详细的软件编程规范,并培训每一位程序员,最终的结果把编码阶段的错误降至10%左右,同时也降低了程序的测试费用,效果相当显著。
本文从代码的可维护性(可读、可理解性、可修改性)、代码逻辑与效率、函数(模块)接口、可测试性四个方面阐述了软件编程规范,规范分成规则和建议两种,其中规则部分为强制执行项目,而建议部分则不作强制,可根据习惯取舍。

2. 编码规范
2.1. 排版风格
<规则 1> 程序块采用缩进风格编写,缩进为4个空格位。排版不混合使用空格和TAB键。
<规则2> 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如->),后不应加空格。
采用这种松散方式编写代码的目的是使代码更加清晰。例如:
(1) 逗号、分号只在后面加空格
printf("%d %d %d" , a, b, c);

(2)比较操作符, 赋值操作符"="、 "+=",算术操作符"+"、"%",逻辑操作符"&&"、"&",位域操作符"<<"、"^"等双目操作符的前后加空格
if(lCurrentTime >= MAX_TIME_VALUE)
a = b + c;
a *= 2;
a = b ^ 2;

(3)"!"、"~"、"++"、"--"、"&"(地址运算符)等单目操作符前后不加空格
*pApple = 'a'; // 内容操作"*"与内容之间
flag = !bIsEmpty; // 非操作"!"与内容之间
p = &cMem; // 地址操作"&" 与内容之间
i++; // "++","--"与内容之间

(4)"->"、"."前后不加空格
p->id = pId; // "->"指针前后不加空格

由于留空格所产生的清晰性是相对的,所以,在已经非常清晰的语句中没有必要再留空格,如最内层的括号内侧(即左括号后面和右括号前面)不要加空格,因为在C/C++语言中括号已经是最清晰的标志了。8BR>另外,在长语句中,如果需要加的空格非常多,那么应该保持整体清晰,而在局部不加空格。
最后,即使留空格,也不要连续留两个以上空格(为了保证缩进和排比留空除外)。

<规则3> 函数体的开始,类的定义,结构的定义,if、for、do、while、switch及case语句中的程序都应采用缩进方式,憑捄蛻}捰禀独占一行并且位于同一列,同时与引用它们的语句左对齐
例如下例不符合规范。
for ( ... ) {
... // 程序代码
}

if ( ... )
{
... // 程序代码
}

void DoExam( void )
{
... // 程序代码
}

应如下书写。
for ( ... )
{
... // 程序代码
}

if ( ... )
{
... // 程序代码
}

void DoExam( void )
{
... // 程序代码
}

<规则4> 功能相对独立的程序块之间或for、if、do、while、switch等语句前后应加一空行。
例如以下例子不符合规范。
例一:
if ( ! ValidNi( ni ) )
{
... // 程序代码
}
nRepssnInd = SsnData[ index ].nRepssnIndex ;
nRepssnNi = SsnData[ index ].ni ;

例二:
char *pContext;
int nIndex;
long lCounter;
pContext = new (CString);
if(pContext == NULL)
{
return FALSE;
}

应如下书写
例一:
if ( ! ValidNi( ni ) )
{
... // 程序代码
}

nRepssnInd = SsnData[ index ].nRepssnIndex ;
nRepssnNi = SsnData[ index ].ni ;

例二:
char *pContext;
int nIndex;
long lCounter;

pContext = new (CString);
if(pContext == NULL)
{
return FALSE;
}

<规则5> if、while、for、case、default、do等语句自占一行。
示例:如下例子不符合规范。
if(pUserCR == NULL) return;

应如下书写:
if( pUserCR == NULL )
{
return;
}

<规则6> 若语句较长(多于80字符),可分成多行写,划分出的新行要进行适应的缩进,使排版整齐,语句可读。
memset(pData->pData + pData->nCount, 0,
(m_nMax - pData->nCount) * sizeof(LPVOID));

CNoTrackObject* pValue =
(CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);

for ( i = 0, j = 0 ; ( i < BufferKeyword[ WordIndex ].nWordLength )
&& ( j < NewKeyword.nWordLength ) ; i ++ , j ++ )
{
... // 程序代码
}

<规则7> 一行最多写一条语句。
示例:如下例子不符合规范。
rect.length = 0 ; rect.width = 0 ;
rect.length = width = 0;

都应书写成:
rect.length = 0 ;
rect.width = 0 ;
<规则8> 对结构成员赋值,等号对齐。
示例:
rect.top = 0;
rect.left = 0;
rect.right = 300;
rect.bottom = 200;
<规则9> #define的各个字段对齐
以下示例不符合规范
#define MAX_TASK_NUMBER 100
#define LEFT_X 10
#define BOTTOM_Y 400

应书写成:
#define MAX_TASK_NUMBER     100
#define LEFT_X                       10
#define BOTTOM_Y                 400

<规则10> 不同类型的操作符混合使用时,使用括号给出优先级。
如本来是正确的代码:
if( year % 4 == 0 || year % 100 != 0 && year % 400 == 0 )

如果加上括号,则更清晰。
if((year % 4) == 0 || ((year % 100) != 0 && (year % 400) == 0))

2.2. 可理解性
2.2.1.注释
注释的原则是有助于对程序的阅读理解,注释不宜太多也不能太少,太少不利于代码理解,太多则会对阅读产生干扰,因此只在必要的地方才加注释,而且注释要准确、易懂、尽可能简洁。注释量一般控制在30%到50%之间。

<规则1> 程序在必要的地方必须有注释,注释要准确、易懂、简洁。
例如如下注释意义不大。
/* 如果bReceiveFlag 为 TRUE */
if ( bReceiveFlag == TRUE)

而如下的注释则给出了额外有用的信息。
/* 如果mtp 从连接处获得一个消息*/
if ( bReceiveFlag == TURE)

<规则2> 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。 示例:如下例子不符合规范。
例子1
/* 获得系统指针和网络指针的副本 */


nRepssnInd = SsnData[ index ].nRepssnIndex ;
nRepssnNi = SsnData[ index ].ni ;

例子2
nRepssnInd = SsnData[ index ].nRepssnIndex ;
nRepssnNi = SsnData[ index ].ni ;
/*获得系统指针和网络指针的副本 */

应如下书写
/*获得系统指针和网络指针的副本 */
nRepssnInd = SsnData[ index ].nRepssnIndex ;
nRepssnNi = SsnData[ index ].ni ;

<规则3> 对于所有的常量,变量,数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,在声明时都必须加以注释,说明其含义。
示例:
/* 活动任务的数量 */
#define MAX_ACT_TASK_NUMBER 1000

#define MAX_ACT_TASK_NUMBER 1000 /*活动任务的数量 */

/* 带原始用户信息的SCCP接口 */
enum SCCP_USER_PRIMITIVE
{
N_UNITDATA_IND , /* 向SCCP用户报告单元数据已经到达 */
N_UNITDATA_REQ , /* SCCP用户的单元数据发送请求 */
} ;

<规则4> 头文件、源文件的头部,应进行注释。注释必须列出:文件名、作者、目的、功能、修改日志等。
例如:
/*********************************************
文件名:
编写者:
编写日期:
简要描述:
修改记录:
********************************************/

说明:摷蛞 枋鰯一项描述本文件的目的和功能等。撔薷募锹紨是修改日志列表,每条修改记录应包括修改日期、修改者及修改内容简述。

<规则5> 函数头部应进行注释,列出:函数的目的、功能、输入参数、输出参数、修改日志等。
形式如下:
/*************************************************
函数名称:
简要描述: // 函数目的、功能等的描述
输入: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系,
输出: // 输出参数的说明, 返回值的说明
修改日志:
*************************************************/
对一些复杂的函数,在注释中最好提供典型用法。

<规则6> 仔细定义并明确公共变量的含义、作用、取值范围及使用方法。
在对变量声明的同时,应对其含义、作用、取值范围及使用方法进行注释说明,同时若有必要还应说明与其它变量的关系。明确公共变量与操作此公共变量的函数或过程的关系,如访问、修改及创建等。
示例:
/* SCCP转换时错误代码 */
/* 全局错误代码,含义如下 */ // 变量作用、含义
/* 0 - 成功 1 - GT 表错误 2 -GT 错误 其它值- 未使用 */ // 变量取值范围

<规则7> 对指针进行充分的注释说明,对其作用、含义、使用范围、注意事项等说明清楚。
在对指针变量、特别是比较复杂的指针变量声明时,应对其含义、作用及使用范围进行注释说明,如有必要,还应说明其使用方法、注意事项等。
示例:
/* 学生记录列表的头指针 */
/* 当在此模块中创建该列表时,该头指针必须初始化, */
/* 这样可以利用GetListHead()获得这一列表。*/ //指针作用、含义
/* 该指针只在本模块使用,其它模块通过调用GetListHead()获取*/
/* 当使用时必须保证它非空 */ //使用范围、方法
STUDENT_RECORD *pStudentRecHead;

<规则8> 对重要代码段的功能、意图进行注释,提供有用的、额外的信息。并在该代码段的结束处加一行注释表示该段代码结束。
示例:
/* 可选通道的组合 */
if ((gsmBCIe31->radioChReq >= DUAL_HR_RCR)
&& (gsmBCIe32->radioChReq >= DUAL_HR_RCR))
{
gsmBCIe31->radioChReq = FR_RCR;
gsmBCIe32->radioChReq = FR_RCR;
}
else if ((gsmBCIe31->radioChReq >= DUAL_HR_RCR)
&& (gsmBCIe32->radioChReq == FR_RCR) )
{
gsmBCIe31->radioChReq = FR_RCR;
}
else if ((gsmBCIe31->radioChReq == FR_RCR)
&& (gsmBCIe32->radioChReq >= DUAL_HR_RCR))
{
gsmBCIe32->radioChReq = FR_RCR;
}
/* 本块结束 ( 可选通道组合 ) */

<规则9> 在switch语句中,对没有break语句的case分支加上注释说明。
示例:
switch(SubT30State)
{
case TA0:
AT(CHANNEL, "AT+FCLASS=1\r", 0);
if(T30Status != 0)
{
return(1);
}
InitFax(); /* 准备发送传真 */
AT(CHANNEL, "ATD\r",-1); /*发送CNG ,接收 CED 和 HDLC 标志*/
T1_Flg = 1;
iResCode = 0;
/* 没有 break; */

case TA1:
iResCode = GetModemMsg(CHANNEL);
break;

default:
break;
}

<规则 10> 维护代码时,要更新相应的注释,删除不再有用的注释。
保持代码、注释的一致性,避免产生误解。

2.2 命名
本文列出Visual C++的标识符命名规范。

<规则 1> 标识符缩写
形成缩写的几种技术:
1) 去掉所有的不在词头的元音字母。如screen写成scrn, primtive写成prmv。
2) 使用每个单词的头一个或几个字母。如Channel Activation写成ChanActiv,Release Indication写成RelInd。
3) 使用变量名中每个有典型意义的单词。如Count of Failure写成FailCnt。
4) 去掉无用的单词后缀 ing, ed等。如Paging Request写成PagReq。
5) 使用标准的或惯用的缩写形式(包括协议文件中出现的缩写形式)。如BSIC(Base Station Identification Code)、MAP(Mobile Application Part)。

关于缩写的准则:
1) 缩写应该保持一致性。如Channel不要有时缩写成Chan,有时缩写成Ch。Length有时缩写成Len,有时缩写成len。
2) 在源代码头部加入注解来说明协议相关的、非通用缩写。
3) 标识符的长度不超过32个字符。
<规则2> 变量命名约定
参照匈牙利记法,即
[作用范围域前缀] + [前缀] + 基本类型 + 变量名
其中:
前缀是可选项,以小写字母表示;
基本类型是必选项,以小写字母表示;
变量名是必选项,可多个单词(或缩写)合在一起,每个单词首字母大写。
前缀列表如下:
前缀 意义 举例
g_ Global 全局变量 g_MyVar
m_ 类成员变量 或 模块级变量 m_ListBox, m_Size
s_ static 静态变量 s_Count
h Handle 句柄 hWnd
p Pointer 指针 pTheWord
lp Long Point 长指针 lpCmd
a Array 数组 aErr
基本类型列表如下:
基本类型 意义 举例
b Boolean 布尔 bIsOK
by Byte 字节 byNum
c Char 字符 cMyChar
i或n Intger 整数 nTestNumber
u Unsigned integer 无符号整数 uCount
ul Unsigned Long 无符号长整数 ulTime
w Word 字 wPara
dw Double Word 双字 dwPara
l Long 长型 lPara
f Float 浮点数 fTotal
s String 字符串 sTemp
sz NULL结束的字符串 szTrees
fn Funtion 函数 fnAdd
enm 枚举型 enmDays
x,y x,y坐标

<规则3> 宏和常量的命名
宏和常量的命名规则:单词的字母全部大写,各单词之间用下划线隔开。命名举例:
#define MAX_SLOT_NUM 8
#define EI_ENCR_INFO 0x07

const int MAX_ARRAY

<规则4> 结构和结构成员的命名
结构名各单词的字母均为大写,单词间用下划线连接。可用或不用typedef,但是要保持一致,不能有的结构用typedef,有的又不用。如:
typedef struct LOCAL_SPC_TABLE_STRU
{
char cValid;
int nSpcCode[MAX_NET_NUM];
} LOCAL_SPC_TABLE ;
结构成员的命名同变量的命名规则。

<规则5> 枚举和枚举成员的命名
枚举名各单词的字母均为大写,单词间用下划线隔开。
枚举成员的命名规则:单词的字母全部大写,各单词之间用下划线隔开;要求各成员的第一个单词相同。命名举例:
typdef enum
{
LAPD_ MDL_ASSIGN_REQ,
LAPD_MDL_ASSIGN_IND,
LAPD_DL_DATA_REQ,
LAPD_DL_DATA_IND,
LAPD_DL_UNIT_DATA_REQ,
LAPD_DL_UNIT_DATA_IND,
} LAPD_PRMV_TYPE;

<规则6> 类的命名
前缀 意义 举例
C 类 CMyClass
CO COM类 COMMyObjectClass
CF COM class factory CFMyClassFactory
I COM interface class IMyInterface
CImpl COM implementation class CImplMyInterface

<规则7> 函数的命名
单词首字母为大写,其余均为小写,单词之间不用下划线。函数名应以一个动词开头,即函数名应类似摱 鼋峁箶。命名举例:
void PerformSelfTest(void) ;
void ProcChanAct(MSG_CHAN_ACTIV *pMsg, UC MsgLen);

2.3. 可维护性
<规则1> 在逻辑表达式中使用明确的逻辑判断。
示例:如下逻辑表达式不规范。
1) if ( strlen(strName) )
2) for ( index = MAX_SSN_NUMBER; index ; index -- )
3) while ( p && *p ) // 假设p为字符指针

应改为如下:
1) if ( strlen(strName) != 0 )
2) for ( index = MAX_SSN_NUMBER; index != 0 ; index -- )
3) while ((p != NULL) && (*p != '\0' ))

<规则2> 预编译条件不应分离一完整的语句。
不正确:
if (( cond == GLRUN)
#ifdef DEBUG
|| (cond == GLWAIT)
#endif
)
{

}

正确:
#ifdef DEBUG
if( cond == GLRUN || cond == GLWAIT )
#else
if( cond == GLRUN )
#endif
{

}

<规则3> 在宏定义中合并预编译条件。
不正确:
#ifdef EXPORT
for ( i = 0; i < MAX_MSXRSM; i++ )
#else
for ( i = 0; i < MAX_MSRSM; i++ )
#endif

正确:
头文件中:
#ifdef EXPORT
#define MAX_MS_RSM MAX_MSXRSM
#else
#define MAX_MS_RSM MAX_MSRSM
#endif
源文件中:
for( i = 0; i < MAX_MS_RSM; i++ )

<规则4> 使用宏定义表达式时,要使用完备的括号。
如下的宏定义表达式都存在一定的隐患。
#define REC_AREA(a, b) a * b
#define REC_AREA(a, b) (a * b)
#define REC_AREA(a, b) (a) * (b)

正确的定义为:
#define REC_AREA(a, b) ((a) * (b))

<规则5> 宏所定义的多条表达式应放在大括号内。
示例:下面的语句只有宏中的第一条表达式被执行。为了说明问题,for语句的书写稍不符规范。
#define INIT_RECT_VALUE( a, b ) \
a = 0 ; \
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 ) ;
}


<规则6> 宏定义不能隐藏重要的细节,避免有return,break等导致程序流程转向的语句。
如下例子是不规范的应用,其中隐藏了程序的执行流程。
#define FOR_ALL for(i = 0; i < SIZE; i++)

/* 数组 c 置0 */
FOR_ALL
{
c[i] = 0;
}

#define CLOSE_FILE { \
fclose(fp_local); \
fclose(fp_urban); \
return; \
}

<规则7> 使用宏时,不允许参数发生变化。
下面的例子隐藏了重要的细节,隐含了错误。
#define SQUARE ((x) * (x))
.
.
.
w = SQUARE(++value);
这个引用将被展开称:
w = ((++value) * (++value));
其中value累加了两次,与设计思想不符。正确的用法是:
w = SQUARE(x);
x++;

<规则8> 当if、while、for等语句的程序块为摽諗时,使用搟}敺 拧_
while ( *s++ == *t++ ) ;
以上代码不符合规范,正确的书写方式为:
while( *s++ == *t++ )
{
/* 无循环体 */
}

while( *s++ == *t++ )
{
}

<规则9> 结构中元素布局合理,一行只定义一个元素。
如下例子不符合规范,
typedef struct
{
_UI left, top, right, bottom;
} RECT;

应书写称:
typedef struct
{
_UI left; /* 矩形左侧 x 坐标 */
_UI top;
_UI right;
_UI bottom;
} RECT;

<规则10> 枚举值从小到大顺序定义。
<规则11> 包含头文件时,使用撓喽月肪稊,不使用摼 月肪稊。
如下引用:
#i nclude "c:\switch\inc\def.inc"

应改为:
#i nclude "inc\def.inc"

#i nclude "def.inc"

<规则12> 不允许使用复杂的操作符组合等。
下面用法不好,
iMaxVal = ( (a > b ? a : b) > c ? (a > b ? a : b) : c );

应该为:
iTemp = ( a > b ? a : b);
iMaxVal = (iTemp > b ? iTemp : b);

不要把"++"、"--"操作符与其他如"+="、"-="等组合在一起形成复杂奇怪的表达式。如下的表达式那以理解。
*pStatPoi++ += 1;
*++pStatPoi += 1;

应分别改为:
*pStatPoi += 1;
pStatPoi++;

++pStatPoi;
*pStatPoi += 1;

<规则13> 函数和过程中关系较为紧密的代码尽可能相邻。
如初始化代码应放在一起,不应在中间插入实现其它功能的代码。以下代码不符合规范,
for (uiUserNo = 0; uiUserNo < MAX_USER_NO; uiUserNo++)
{
...; /* 初始化用户数据 */
}

pSamplePointer = NULL;
g_uiCurrentUser = 0; /* 设置当前用户索引号 */

应必为:
for (uiUserNo = 0; uiUserNo < MAX_USER_NO; uiUserNo++)
{
...; /* 初始化用户数据 */
}

g_uiCurrentUser = 0; /* 设置当前用户索引号 */

pSamplePointer = NULL;

<规则14> 每个函数的源程序行数原则上应该少于200行。
对于消息分流处理函数,完成的功能统一,但由于消息的种类多,可能超过200行的限制,不属于违反规定。

<规则15> 语句嵌套层次不得超过5层。
嵌套层次太多,增加了代码的复杂度及测试的难度,容易出错,增加代码维护的难度。

<规则16> 用sizeof来确定结构、联合或变量占用的空间。
这样可提高程序的可读性、可维护性,同时也增加了程序的可移植性。

<规则17> 避免相同的代码段在多个地方出现。
当某段代码需在不同的地方重复使用时,应根据代码段的规模大小使用函数调用或宏调用的方式代替。这样,对该代码段的修改就可在一处完成,增强代码的可维护性。

<规则18> 使用强制类型转换。
示例:
USER_RECORD *pUser;

pUser = (USER_RECORD *) malloc (MAX_USER * sizeof(USER_RECORD));

<规则19> 避免使用 goto 语句。
<规则20> 避免产生摮绦蚪釘(program knots),在循环语句中,尽量避免break、goto的使用。
如下例子:
for( i = 0; i < n; i++)
{
bEof = fscanf( pInputFile, "%d;", &x[i]);
if( bEof == EOF )
{
break;
}
nSum += x[i];
}

最好按以下方式书写,避免程序打摻釘:
for( i = 0; i < n && bEof= EOF; i++)
{
bEof = fscanf( pInputFile, "%d;", &x[i]);
if( bEof!= EOF )
{
nSum += x[i];
}
}

<规则21> 功能相近的一组常量最好使用枚举来定义。
不推荐定义方式:
/* 功能寄存器值 */
#define ERR_DATE 1 /* 日期错误 */
#define ERR_TIME 2 /* 时间错误 */
#define ERR_TASK_NO 3 /* 任务号错误 */

推荐按如下方式书写:
/*功能寄存器值 */
enum ERR_TYPE
{
ERR_DATE = 1, /*日期错误 */
ERR_TIME = 2, /*时间错误 */
ERR_TASK_NO = 3 /* 任务号错误 */
}

<规则22> 每个函数完成单一的功能,不设计多用途面面俱到的函数。
多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。
使函数功能明确化,增加程序可读性,亦可方便维护、测试。

<建议1> 循环、判断语句的程序块部分用花括号括起来,即使只有一条语句。
如:
if( bCondition == TRUE )
bFlag = YES;

建议按以下方式书写:
if( bCondition == TRUE )
{
bFlag = YES;
}
这样做的好处是便于代码的修改、增删。

<建议2> 一行只声明一个变量。
不推荐的书写方式:
void DoSomething(void)
{
int Amicrtmrs, nRC;
int nCode, nStatus;

推荐做法:
void DoSomething(void)
{
int nAmicrtmrs; /* ICR 计时器 */
int nRC; /* 返回码 */
int nCode; /* 访问码 */
int nStatus; /* 处理机状态 */

<建议3> 使用专门的初始化函数对所有的公共变量进行初始化。
<建议4> 使用可移植的数据类型,尽量不要使用与具体硬件或软件环境关系密切的变量。

<建议5> 用明确的函数实现不明确的语句功能

示例:如下语句的功能不很明显。
value = ( a > b ) ? a : b ;

改为如下就很清晰了。

int max( int a, int b )
{
return ( ( a > b ) ? a : b ) ;
}

value = max( a, b ) ;

或改为如下。
#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )

value = MAX( a, b ) ;

2.4. 程序正确性、效率
<规则1> 严禁使用未经初始化的变量。
引用未经初始化的变量可能会产生不可预知的后果,特别是引用未经初始化的指针经常会导致系统崩溃,需特别注意。声明变量的同时初始化,除了能防止引用未经初始化的变量外,还可能生成更高效的机器代码。

<规则2> 定义公共指针的同时对其初始化。
这样便于指针的合法性检查,防止应用未经初始化的指针。建议对局部指针也在定义的同时初始化,形成习惯。

<规则3> 较大的局部变量(2K以上)应声明成静态类型(static),避免占用太多的堆栈空间。
避免发生堆栈溢出,出现不可预知的软件故障。

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

A.数组越界。
char aMyArray[10];
for( i = 0; i <= 10; i++ )
{
aMyArray[i] = 0; //当i等于10时,将发生越界。
}

B.指针操作越界。
char aMyArray[10];
char *pMyArray;

pMyArray = aMyArray;
--pMyArray; // 越界

pMyArray = aMyArray;
pMyArray += 10; // 越界

<规则5> 减少没必要的指针使用,特别是较复杂的指针,如指针的指针、数组的指针,指针的数组,函数的指针等。
用指针虽然灵活,但也对程序的稳定性造成一定威胁,主要原因是当要操作一个指针时,此指针可能正指向一个非法的地址。安安全全地使用一个指针并不是一件容易的事情。

<规则6> 防止引用已经释放的内存空间。
在实际编程过程中,稍不留心就会出现在一个模块中释放了某个内存块(如指针),而另一模块在随后的某个时刻又使用了它。要防止这种情况发生。

<规则7> 程序中分配的内存、申请的文件句柄,在不用时应及时释放或关闭。
分配的内存不释放以及文件句柄不关闭,是较常见的错误,而且稍不注意就有可能发生。这类错误往往会引起很严重后果,且难以定位。

<规则8> 注意变量的有效取值范围,防止表达式出现上溢或下溢。
示例:
unsigned char cIndex = 10;
while( cIndex-- >= 0 )
{
} //将出现下溢
当cIndex等于0 时,再减1不会小于0,而是0xFF,故程序是一个死循环。

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

<规则9> 防止精度损失。
以下代码将产生精度丢失。
#define DELAY_MILLISECONDS 10000
char time;
time = DELAY_MILLISECONDS;
WaitTime( time );
代码的本意是想产生10秒钟的延时,然而由于time为字符型变量,只取DELAY_MILLISECONDS的低字节,高位字节将丢失,结果只产生了16毫秒的延时。

<规则10> 防止操易混淆的作符拼写错误。
形式相近的操作符最容易引起误用,如C/C++中的“=斢霌==敗 |斢霌||敗 &斢霌&&數龋 羝葱创砹耍 嘁肫鞑灰欢 芄患觳槌隼础_
示例:如把“&斝闯蓳&&敚 蚍粗 _
bRetFlag = ( pMsg -> bRetFlag & RETURN_MASK ) ;
被写为:
bRetFlag = ( pMsg -> bRetFlag && RETURN_MASK ) ;
<规则11> 使用无符号类型定义位域变量。
示例:
typedef struct
{
int bit1 : 1;
int bit2 : 1;
int bit3 : 1;
} bit;

bit.bit1 = 1;
bit.bit2 = 3;
bit.bit3 = 6;
printf("%d, %d, %d", bit.bit1, bit.bit2, bit.bit3 );

输出结果为:-1,-1, -2,不是: 1,3,6.