1. 排版
规则:较长的语句、表达式等要分成多行书写。
建议:一行程序以小于120字符为宜。
规则:循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分。
建议:长表达式在低优先级操作符处划分新行
规则:若函数的参数较长,则要进行适当的划分。
实例
os_iSetFlag( T_OR, (ulong *)&lPrevPattern,
stEventSetInfo[iEvCnt].iEventFlagHndl,
stEventSetInfo[iEvCnt].iEventFlagPtn ) ;
规则:不允许把多个短语句写在一行中,即一行只写一条语句。
2. 注释
规则:边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。
规则:对代码的注释应放在其上方相邻位置,不可放在下面。
规则:注释的目的是解释代码的目的、功能和采用的方法,提供代码以外的信息,帮助读者理解代码,防止没必要的重复注释信息。
实例:如下注释意义不大
/*リクエストNO*/
stRPRequestInfo.uiRequestNum = pstRouteCalcManage->uiRequestNo;
规则:注释与前面的执行语句之间空一行。
实例:
ASSERT( os_iStackErrorCheck( ESTMATE_SIZE ) == OK ) ;
ASSERT( pulDistance != NULL ) ;
建议:在程序块的结束行右方加注释标记,以表明某程序块的结束。
实例:
} /* End switch ( expression ) */
规则:源文件的开头要有注释,注释的格式参见源文件模板。所有的项目都要写,有的项目没有时,说明"无"。注释可以采用英语、日语。
/*********************************************************************
* FILE名 : gc00main.c
* 日付 : 97/09/04
* 作成者 : 安東早和子
* 対象MODEL : REF
*
* 機能概略 : 案内制御
*
*---------------------------------------------------------------------
* 改訂履歴
* 番号 日付 修正者 関数名
*
**********************************************************************/
规则:写函数全体的注释框时,如果使用了如下项目,则必需记述(注释可用日文或英文)。
n Global Variable(全程变量)
n File Static Variable(文件Static变量)
n Function Static Variable(函数内Static变量)
规则:函数的注释框,必须写以下项目。
n Function Name(函数名)
n Description(处理记述)
n Return Code(返回值)
n Return Code Judgement(有无判断错的必要性)
n Data(制作日期)
n Author(制作者,公司名)
规则:应对不易理解的分枝条件表达式加注释。
规则:不易理解的循环, 应说明对出口条件。
3. 可读性
规则:注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。
规则:避免使用不易理解的数字,用有意义的标识来替代。
说明:在C语言中可用宏来替代无意义的数字。
规则:不要使用难懂的技巧性很高的语句。
建议:源程序中关系较为紧密的代码应尽可能相邻。
说明:便于程序阅读和查找。
实例:以下代码布局不太合理。
rect.length = 10 ;
char_poi = str ;
rect.width = 5 ;
4. 文件
说明:一般说来,源文件的整体构成为以下内容(有时可能不存在某些部分)。
n 源文件的注释框
n 包含文件部分
n 宏定义部分
n 类型定义部分
n 结构体定义部分
n 全局变量定义部分
n 文件static变量定义部分
n 函数原型声明
n 调试开关定义部分
n 函数定义部分
建议:一个源文件的最大行数为800行。
规则:各节之前要有表示节开始的注释框,参见源文件模板:
规则:头文件应采用#ifndef/#define/#endif的方式来防止多次被包含,其中使用的宏名为:"__"+"文件名"+"_"+"扩展名",文件名与扩展名均为大写。
5. 命名标准
5.1 命名的一般方针
规则:各单词的开头用大写字母,其余用小写字母,省略用语除外。
实例: 函数sys_vWaitAppointedModuleCompleted()。
规则:禁止以大写字母/小写字母的不同来区别命名。
实例: 如不允许使用iSomething和iSOMETHING表示不同的变量。
规则:禁止在数据命名的一部分插入数字区别数据(数学公式例外)。
实例: 如不允许使用x1,x2,x3,x4表示不同的变量。
建议:进行命名的时候,在充分把握数据对象(变量、函数等)的内容含义的基础上,进行能明确显示其内容的命名。即做到望文生义。
实例: 如用iReturnCode来取得函数的返回值,一目了然。
建议:模块对外提供的宏、变量、类型、结构体、函数等,最好在前面加上模块名。
实例:
#define DDL_MEDIA_STAT_NONE (0)
其中DDL是模块名Disk DB Loader的缩写
5.2 变量/函数的命名
规则:变量、函数名的构成要素:
[(范围限定符)]+(类型)+(标识符)
实例: 如os_iStackErrorCheck(),其中os_是范围限定符,表明本函数是属于os层的系统调用,而'i'是类型符,表示返回值是整型。
规则:类型使用以下字母(全部是小写字母)。
规则:指针、数组的类型各自使用‘p'‘a',含指针类型的变量、函数名的命名方法遵照以下规定。
数组指针
[(范围限定符)]+pa+(类型)+(标识符)
指针数组
[(范围限定符)]+ap+(类型)+(标识符)
说明:对在多个源文件之间共同使用的全程变量、函数必须使用范围限定符,对只在单一源文件内使用的局部变量、函数使用与否都可以。范围限定符有各模块统一制定。
建议:对所有的函数使用范围限定符,提供给外部模块使用的函数限定符使用全大写字母,模块内部使用的函数限定符使用全小写字母。
建议:用户自定义的类型原则上可不使用类型标识。
5.3 结构、联合的命名
规则:结构体一定要按照‘typedef'预先作为一个类型进行说明。
实例:
typedef struct {
int iKind;
int iRouteStat;
ushort usRouteNo;
} gC_Mail_t;
规则:结构体类型命名的末尾要加‘_t'。
5.4 宏的命名
规则:宏命名都用大写字母写。
规则:构成宏命名的各词汇间用‘_'(下划线)隔开。
实例:
#define BRWS_NAVIKEYWD_STR_NAME ("NAME")
6. 定义的位置
规则:多个源文件使用的宏、用户自定义类型、结构体要在头文件内定义。
规则:在一个源文件内使用宏、用户自定义类型、结构体在源文件内相应的节中定义。
建议:只在一个函数内使用的用户自定义类型、结构体可以在各个函数内部定义。
7. 宏定义和预处理命令
规则:充分利用宏定义,尽量不使用直接的数值。
规则:所有的预处理器命令从第一列开始写。但是在"#if"的嵌套内部的预处理器命令语句缩进。
建议:对于所有的宏,使用行末注释表示宏使用目的。
规则:把宏内使用的所有的变量以括号括起来。
实例:
#define SIGN(_a_) (((_a_)<ZERO)?(-1)1))
规则:把记述的宏全体以括号括起来。
实例:
#define BRWS_NAVIDATA_TYPE_STRING (0)
规则:将宏所定义的多条表达式放在大括号中。
实例:下面的语句只有宏中的第一条表达式被执行。为了说明问题,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 ; /
}
规则:使用宏时,不允许参数发生变化。
实例:如下用法可能导致错误。
#define SQUARE( a ) ( ( a ) * ( a ) )
int a = 5 ;
int b ;
b = SQUARE( a++ ) ; // 结果:a = 7,即执行了两次增1。
正确的用法是:
b = SQUARE( a ) ;
a ++ ; // 结果:a = 6,即执行了一次增1。
建议:为避免重复定义,最好在宏名前加上模块名。
规则:不允许定义和系统头文件中同名的宏,包括Windows、VC中的宏,如BYTE、LOWORD等。
8. 用户自定义类型
规则:用户自定义类型必须以‘tpyedef'进行说明,不允许以宏定义进行说明。
规则:用户的定义型,在名字的最后加‘_t'。
9. 结构体
建议:结构体成员的个数不要太多,建议不超过15个。
建议:关联性强的成员可不使用构造体说明,使构造体嵌套。构造提嵌套的深度的最大值为3。
说明:上述的限制内的说明不可能时,或增加成员个数,或分割构造体,但尽量不要加深嵌套。
规则:用户的定义型,在名字的最后加‘_t'。
规则:合理排列结构中成员的顺序,可节省空间并增加可理解性。在Windows和导航机中,结构中的各成员的顺序不同,结构的长度也不同。建议结构体中的成员按照由小到大的顺序排列。
实例:
struct {
char c1;
int i1;
char c2
} stMyStruct1;
struct {
char c1;
char c2;
int i1;
} stMyStruct2;
stMyStruct1将占12字节,而stMyStruct2占8字节。
10. 变量
规则:一行只作一个变量定义。但是象坐标x、y、z,纬度、经度等对同时进行定义有非常重要意义的在一行可进行多个变量定义。
建议:原则上变量说明的后面加行末注释。但是变量名能充分表达变量内容时也可省略。
建议:原则上按以下顺序定义变量
¨ 用户自定义类型
¨ 文件指针
¨ 构造体
¨ 共用体
¨ signed char(但是char表示signed char时unsigned char)
¨ unsigned short
¨ unsignedlong
¨ char
¨ short
¨ long
规则:char型变量的string长度要使用宏定义其字符串的最大字符数,用(宏)+1说明。
实例:
#define STRING_LENTH 256
char cString[STRING_LENTH+1];
建议:原则上禁止在多个源文件间使用全局变量。但是由于处理上的需要不能不使用时,按照以下规定使用。
规则:只把各任务或任务组共同使用的构造体数据作为全局对象。不对只在一个源文件内使用的数据或不是构造体的变量进行全局化。
规则:在命名全局变量时附加上前面5.2讲过的范围限定符。
规则:定义全局变量时必须附加以下注释。
¨ 变量保存的数据的内容。
¨ 初始化条件。
¨ 变更条件。
规则:在定义全局变量时不能进行初始化。
规则:防止局部变量与公共变量同名。
规则:严禁使用未经初始化的变量。
说明:特别是在C/C++中引用未经赋值的指针,经常会引起系统崩溃。
建议:对编译系统默认的数据类型转换,也要有充分的认识。
实例:如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。
char chr ;
unsigned short int exam ;
chr = -1 ;
exam = chr ; // 编译器不产生告警,此时exam为0xFFFF。
建议:尽量减少没有必要的数据类型默认转换与强制转换。
规则:不要声明过大的局部变量,以免堆栈溢出,可以定义成static全局变量或从内存池中分配。
规则:不要返回局部变量的地址。
说明:因为当函数退出时,非static局部变量将消失,所以引用返回的指针将可能引起严重后果。
规则:用unsigned定义不可能为负数的变量。
规则:变量的使用目的只能有一个,禁止以多个目的使用变量。
11. 操作符
建议:不要把"++"、"--"操作符与其它操作符如"+="、"-="等组合在一起形成较奇怪的表达式。
建议:位运算降低代码的可读性,尽量少使用。
12. 指针
规则:当对指针进行"++"、"--"与"+="、"-="操作时,要注意(1)指针地址的改变是以所指向数据类型的大小为单位的。(2)时刻留心指针所指向的地址是否越出所允许的范围(即是否越界)。
13. 函数
建议:注意控制参数的数量,一般来说不要超过7个,当参数过多时,应该考虑将参数定义为一个结构体,并且将结构体指针作为参数。
建议:函数的大小定为60行以内(除去注释,空行,变量定义,调试开关等)。
建议:一个函数仅完成一件功能。
建议:为简单功能编写函数。
说明:虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。
实例:如下语句的功能不很明显。
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 ) ;
规则:防止将函数的参数作为工作变量。
说明:将函数的参数作为工作变量,有可能错误地改变参数内容,所以很危险。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
实例:下函数的实现不太好。
void sum_data( unsigned int num, int * data, int * sum )
{
unsigned int count ;
*sum = 0 ;
for ( count = 0 ; count < num ; count ++ )
{
*sum += data[ count ] ; // sum成了工作变量,不太好。
}
}
若改为如下,则更好些。
void sum_data( unsigned int num, int * data, int * sum )
{
unsigned int count ;
int sum_temp ;
sum_temp = 0 ;
for ( count = 0 ; count < num ; count ++ )
{
sum_temp += data[ count ] ;
}
*sum = sum_temp ;
}
建议:避免使用无意义或含义不清的动词为函数命名。
说明:避免用含义不清的动词如process、handle等为函数命名,因为这些动词并没有说明要具体做什么。
建议:函数的返回值要清楚、明了,让使用者不容易忽视错误情况。
说明:函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。
规则:对所调用函数的错误返回码要仔细、全面地处理。
建议:在调用函数填写参数时,应尽量减少没有必要的默认数据类型转换或强制数据类型转换。
说明:因为数据类型转换或多或少存在危险。
建议:防止把没有关联的语句放到一个函数中。
说明:防止函数或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程中。随机内聚给函数或过程的维护、测试及以后的升级等造成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境。
在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。
实例:如下函数就是一种随机内聚。
void Init_Var( void )
{
Rect.length = 0 ;
Rect.width = 0 ; // 初始化矩形的长与宽
Point.x = 10 ;
Point.y = 10 ; // 初始化"点"的坐标。
}
矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。
应如下分为两个函数:
void Init_Rect( void )
{
Rect.length = 0 ;
Rect.width = 0 ; // 初始化矩形的长与宽
}
void Init_Point( void )
{
Point.x = 10 ;
Point.y = 10 ; // 初始化"点"的坐标。
}
建议:如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题。
说明:若此段代码各语句之间有实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的函数。
建议:可能在多个任务中调用的函数,要注意可重入性。
规则:编写可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。
规则:编写可重入函数时,若使用全局变量,则应通过信号量保护。对于可能在中断处理程序中访问的全局变量,应该用关中断的方式进行保护。
规则:显示地给出函数的返回值类型,无返回值函数定义为void。
建议:原则上一个函数只有一个return语句,但在函数中间发生致命错误而有必要异常结束时,可以使用return语句。
规则:不使用控制参数,如根据参数决定在函数中走哪个分支。
规则:禁止使用extern声明,提供给其他文件使用的函数在头文件中声明。
13.1 函数内部排版
13.1.1 运算符使用规则
建议:对于复杂的运算,适当地加上括号来表示运算的优先顺序,即使是优先级高的运算加上扩号也没有坏处。
建议:位运算降低代码的可读性,使用位运算时,建议写上详细注释。
建议:将精度高的数据代入到精度低的运算时,将cast(强制转换)运算符明确写在使用其变量的地方,不进行缺省的cast。这样,编译器给出类型从高向低转换丢失精度时,可能会是真正的错误。
13.1.2 控制语句格式
建议:在控制条件语句内不使用复杂的函数,要使用复杂函数返回的值作为控制条件使用时,另外定义表示函数的返回值的变量,并将其变量用于控制条件语句内。
建议:在控制条件语句内不进行复杂的算术运算。可以另外定义变量并且将算术运算的结果赋值给这个变量,将此变量作为判断。
建议:控制条件语句中不写使用超过2个的括号才可以正确表达的复杂的条件。在复杂的条件判断中,定义布尔变量,并在条件语句中使用。
建议:if、while、for语句的单行程序块也应以"{"开始、"}"结束。
说明:防止所使用代表多条表达式的宏定义不规范,并增加可读性。
实例:
for ( dpc_ind = 0 ; dpc_ind < MAX_DPC_NUMBER ; dpc_ind ++ )
{
INIT_DPC_TABLE( dpc_ind ) ;
}
规则:if、while、for、switch及case、default等语句自占一行。
13.1.3 if语句
规则:用==比较变量和常数是否相等时,把常数写在左边,以防止把==写成=
规则:if等条件语句的左花括号一般有两种写法:可以和前面的判断语句写在一行上,也可以写在下一行上。但右花括号一定要单独写一行。
建议:左花括号写在下一行上。
实例:
(具体例1)if语句
if ( iRetCode != SUCCESS ) {
iRetCode = gC_ERROR;
}
(具体例2)if语句(伴有else时)
if ( iRetCode != SUCCESS ) {
iRetCode = gC_ERROR;
}
else {
iRetCode = gC_OK;
}
13.1.4 switch语句
规则:switch语句一定要写default,这个default中只记述例外处理。当无例外处理时,只注明无例外处理的注释而不写代码。
建议:在switch语句中,将比较频繁的处理放在前面,这样可以提高执行的效率。
建议:对没有break语句对应的case语句加以留心,必要时加上注释说明。
说明:有时由于粗心可能在case语句下忘写了break,应防止这种情况发生;有时为了实际情况的应用而不写break语句,这时最好加上注释说明。
13.1.5 for语句
建议:在控制条件语句的初期化语句里,即第一个分号的语句,除了对控制循环的变量的处理(循环计数器的初期化,状态变量,布尔变量以及标志的初期化等)以外,原则上不进行其他的处理。
建议:尽量避免在while/for循环体中申请或释放内存,以防止产生内存碎片。
规则:若无特殊情况,循环计数要从0开始,不要从1开始。结束条件要用<,不要用<=。
13.1.6 while语句
建议:除了任务主函数外,不要使用死循环,即while(1)。如果使用了,写出退出循环的条件。
13.2 对于在控制语句里使用的变量的处理的规章
建议:在控制语句里使用的变量,若需要初期化,则在控制语句的注释的后面控制条件语句的前面进行初期化。
13.3 对于循环语句的规则
建议:LOOP结束后,将loop counter用于控制语句以外的情况下,只允许在控制语句的直接后面使用一次,有必要在其后也要使用时,在控制语句内设置一个专用的新的变量,以或增量(减量)LOOP值,或把控制语句终了后的循环计数器在控制语句之后代入其他函数。
规则:无论LOOP内外,禁止使用GOTO语句。
建议:不使用只以拖延处理为目的的空循环。
13.4 关于递归调用的规则
规则:使用递归时,一定要详细记述说明其处理内容的文档。
建议:减少函数本身或函数间的递归调用。
说明:递归调用特别是函数间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。
规则:函数间的递归调用对他人来说几乎是无法理解的,如果必须使用,一定要有详细的文档。
14. 调试、测试代码
规则:调试开关的定义写在头文件或源文件的调试开关定义section中,以DBG_开头。
规则:测试开关的定义写在头文件或源文件的测试开关定义section中,以TEST_开头。
建议:调测开关应分为不同级别和类型。
说明:调测开关的设置及分类应从以下几方面考虑:针对模块或系统某部分代码的调测;针对模块或系统某功能的调测;出于某种其它目的,如对性能、容量等的测试。这样做便于软件功能的调测,并且便于模块的单元测试、系统联调等。
规则:使用ASSERT来发现软件问题,提高代码可测性。
说明:ASSERT是对某种假设条件进行检查(可理解为若条件成立则无动作,否则应报告),它可以快速发现并定位软件问题,同时对系统错误进行自动报警。ASSERT可以对在系统中隐藏很深,用其它手段极难发现的问题进行定位,从而缩短软件问题定位时间,提高系统的可测性。实际应用时,可根据具体情况灵活地设计ASSERT。
规则:不能用ASSERT来检查最终产品肯定会出现且必须处理的错误情况。
说明:如某模块收到其它模块或链路上的消息后,要对消息的合理性进行检查,此过程为正常的错误检查,不能用ASSERT来实现。
规则:用断言确认函数的参数。
实例:假设某函数参数中有一个指针,那么使用指针前可对它检查,如下。
规则:循环体内工作量最小化。
说明:应仔细考虑循环体内的语句是否可以放在循环体之外,使循环体内工作量最小,从而提高程序的时间效率。
建议:编程时充分利用标准C语言提供的宏,如在调试打印函数中可由系统提供的"__LINE__"及"__FILE__"宏获得行号及文件名信息。
2.9 质量保证
建议:使用变量时要注意其边界值的情况。
实例:如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 ;
规则:编程时,要防止差1错误。
说明:此类错误一般是由于把"<="误写成"<"或">="误写成">"等造成的,由此引起的后果,很多情况下是很严重的,所以编程时,一定要在这些地方小心。当编完程序后,应对这些操作符进行彻底检查。
规则:打开编译器的所有告警开关对程序进行编译,并要消除所有的警告。
建议:某些语句经编译后产生告警,但如果你认为它是正确的,那么应通过某种手段去掉告警信息。
PS:个人以为,仅供参考,有些内容值得商榷.