收藏 分享(赏)

成都信息工程学院非计算机专业C语言初学者编程规范(学生用).doc

上传人:dzzj200808 文档编号:2605215 上传时间:2018-09-23 格式:DOC 页数:26 大小:129.50KB
下载 相关 举报
成都信息工程学院非计算机专业C语言初学者编程规范(学生用).doc_第1页
第1页 / 共26页
成都信息工程学院非计算机专业C语言初学者编程规范(学生用).doc_第2页
第2页 / 共26页
成都信息工程学院非计算机专业C语言初学者编程规范(学生用).doc_第3页
第3页 / 共26页
成都信息工程学院非计算机专业C语言初学者编程规范(学生用).doc_第4页
第4页 / 共26页
成都信息工程学院非计算机专业C语言初学者编程规范(学生用).doc_第5页
第5页 / 共26页
点击查看更多>>
资源描述

1、1成都信息工程学院非计算机专业C 语言初学者编程规范(学生用)成都信息工程学院计算中心对于程序员来说,能工作的代码并不等于“好”的代码。 “好”代码的指标很多,包括易读、易维护、易移植和可靠等。其中,可靠性对嵌入式系统非常重要,尤其是在那些对安全性要求很高的系统中,如飞行器、汽车和工业控制中。这些系统的特点是:只要工作稍有偏差,就有可能造成重大损失或者人员伤亡。一个不容易出错的系统,除了要有很好的硬件设计(如电磁兼容性),还要有很健壮或者说“安全”的程序。然而,很少有程序员知道什么样的程序是安全的程序。很多程序只是表面上可以干活,还存在着大量的隐患。当然,这其中也有 C 语言自身的原因。因为

2、C 语言是一门难以掌握的语言,其灵活的编程方式和语法规则对于一个新手来说很可能会成为机关重重的陷阱。同时,C 语言的定义还并不完全,即使是国际通用的 C 语言标准,也还存在着很多未完全定义的地方。要求所有的嵌入式程序员都成为 C 语言专家,避开所有可能带来危险的编程方式,是不现实的。最好的方法是有一个针对安全性的 C 语言编程规范,告诉程序员该如何做。本规范在制定过程中,主要参考了业界比较推崇的华为软件编程规范和范例和MISRA 2004 规则 ,适合于非计算机专业的 C 语言初学者使用,目的在于在教学中培养学生良好的编程规范和意识、素质,促进所设计程序安全、健壮、可靠、可读与可维护(程序简单

3、、清晰)。考虑到面向的是初学者,为便于教学和课程考核操作,本规范中的要求比较基本。事实上,很多公司都有自己规定的代码风格,包括命名规则、缩进规则等,学生参加工作后,应再进一步学习和应用公司的规范。建议学生在学习本规范的同时,花点时间阅读本规范的参考文献原文,特别是熟读本规范的参考文献之一的“安全第一”的 C 语言编程规范 ,深刻理解编程规范与程序安全、健壮、可靠、可读、可维护间的关系和作用,在学习和工作中养成良好的编程风格。1 排版1.1 严格采用阶梯层次组织程序代码函数或过程的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case 语句下的情况处理语句也要遵从语句缩进要求。程序

4、块的分界符(如 C/C+ 语言的大括号 和)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及 if 、for 、do 、while 、switch 、case 语句中的程序都要采用如上的缩进方式。2各层次缩进的风格采用 TAB 缩进(TAB 宽度原则上使用系统默认值,TC 使用 8 空格宽度,VC 使用 4 空格宽度)。示例:if (x is true)we do yelseif (a b).else.和:if (x = y).else if (x y).else注意,右括号所在的行不应当有其它东西,除非跟随着一个条件判断。也就是 d

5、o-while 语句中的“while” ,象这样:dobody of do-loop while (condition);说明:代码离不开缩进,缩进背后的思想是:清楚地定义一个控制块从哪里开始,到哪里结束。尤其是在你连续不断的盯了 20 个小时的屏幕后,如果你有大尺寸的缩进。你将更容易发现缩进的好处。关于缩进主要有两个争论,一个是该用空格(Space)还是用制表符(Tab),另外一个是该用 4 格缩进还是 8 格缩进甚至都不是。建议总是使用 Tab 缩进,因为几乎所有的代码(不仅仅是 C 代码)都在使用 Tab 缩进。3现在,有些人说 8 个字符大小的缩进导致代码太偏右了,并且在一个 80 字

6、符宽的终端屏幕上看着很不舒服。对这个问题的回答是:如果你有超过 3 个级别的缩进,你就有点犯糊涂了,应当修改你的程序。简而言之,8 个字符的缩进使程序更易读,而且当你把功能隐藏的太深时,多层次的缩进还会对此很直观的给出警告。要留心这种警告信息。例外:对于由开发工具自动生成的代码可以有不一致。1.2 及时折行较长的语句(80 字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进(至少 1 个 TAB 位置),使排版整齐,语句可读。示例:report_or_not_flag = (taskno ),后不应加空格。采用这种松散方式编写代码的目的

7、是使代码更加清晰。由于留空格所产生的清晰性是相对的,所以,在已经非常清晰的语句中没有必要再留空格,如果语句已足够清晰则括号内侧(即左括号后面和右括号前面)不需要加空格,多重括号间不必加空格,因为在 C/C+语言中括号已经是最清晰的标志了。在长语句中,如果需要加的空格非常多,那么应该保持整体清晰,而在局部不加空格。给操作符留空格时不要连续留两个以上空格。(1)逗号、分号只在后面加空格。int a, b, c;(2)比较操作符, 赋值操作符“=“、 “+=“,算术操作符“+“、“%“,逻辑操作符“a *= 2;a = b 2;(3)“!“、“、“+“、“-“、“ / 内容操作“*“与内容之间fla

8、g = !isEmpty; / 非操作“!“与内容之间p = / 地址操作“ / “+“,“-“与内容之间(4)“-“、“.“前后不加空格。5p-id = pid; / “-“指针前后不加空格(5) if、for、while、switch 等与后面的括号间应加空格,使 if 等关键字更为突出、明显。if (a = b repssn_ni = ssn_dataindex.ni;例 2:repssn_ind = ssn_dataindex.repssn_index;repssn_ni = ssn_dataindex.ni;/* get replicate sub system index and

9、net indicator */应如下书写/* get replicate sub system index and net indicator */repssn_ind = ssn_dataindex.repssn_index;repssn_ni = ssn_dataindex.ni;(5)对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方相邻位置或右方。示例:/* active statistic task number */#define MAX_ACT_TASK_NUMBER 1000#define MA

10、X_ACT_TASK_NUMBER 1000 /* active statistic task number */(6)数据结构声明( 包括数组、结构、类、枚举等) ,如果其命名不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。示例:可按如下形式说明枚举/数据/联合结构。/* sccp interface with sccp user primitive message name */enum SCCP_USER_PRIMITIVEN_UNITDATA_IND, /* sccp notify sccp user unit

11、data come */N_NOTICE_IND, /* sccp notify user the No.7 network can not */* transmission this message */N_UNITDATA_REQ, /* sccp users unit data transmission request*/;(7)全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。示例:/* The ErrorCode when SCCP translate */* Global Title failure, as follows */ /

12、变量作用、含义/* 0 SUCCESS 1 GT Table error */* 2 GT error Others no use */ / 变量取值范围8/* only function SCCPTranslate() in */* this modual can modify it, and other */* module can visit it through call */* the function GetGTTransErrorCode() */ / 使用方法BYTE g_GTTranErrorCode;(8)注释与所描述内容进行同样的缩排,让程序排版整齐,并方便注释的阅读与理

13、解。示例:如下例子,排版不整齐,阅读稍感不方便。void example_fun( void )/* code one comments */CodeBlock One/* code two comments */CodeBlock Two应改为如下布局。void example_fun( void )/* code one comments */CodeBlock One/* code two comments */CodeBlock Two(9)将注释与其上面的代码用空行隔开。示例:如下例子,显得代码过于紧凑。/* code one comments */program code one/

14、* code two comments */program code two应如下书写/* code one comments */program code one/* code two comments */program code two(10)对变量的定义和分支语句(条件分支、循环语句等)必须编写注释。这些语句往往是程序实现某一特定功能的关键,对于维护人员来说,良好的注释帮助更好的理解程序,有时甚至优于看设计文档。(11)对于 switch 语句下的 case 语句,如果因为特殊情况需要处理完一个 case 后进入下一个 case 处理(即上一个 case 后无 break),必须在该

15、case 语句处理完、下一个 case 9语句前加上明确的注释,以清楚表达程序编写者的意图,有效防止无故遗漏 break 语句(可避免后期维护人员对此感到迷惑:原程序员是遗漏了 break 语句还是本来就不应该有)。示例:case CMD_DOWN:ProcessDown();break;case CMD_FWD:ProcessFwd();if (.).break; elseProcessCFW_B(); / now jump into case CMD_Acase CMD_A:ProcessA();break;.(12)在程序块的结束行右方加注释标记,以表明某程序块的结束。当代码段较长,特别

16、是多重嵌套时,这样做可以使代码更清晰,更便于阅读。示例:参见如下例子。if (.)program codewhile (index tmp、flag-flg、statistic-stat、increment-inc、message-msg 等缩写能够被大家基本认可。(2)命名中若使用特殊约定或缩写,则要有注释说明。应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。(3)自己特有的命名风格,要自始至终保持一致,不可来回变化。个人的命名风格,在符合所在项目组或产品组的命名规则的前提下,才可使用。(即命名规则中没有规定到的地方才可有个人命名风格)。(4)对于变

17、量命名,禁止取单个字符(如 i 、j 、k. ),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但 i 、j 、 k 作局部循环变量是允许的。变量,尤其是局部变量,如果用单个字符表示,很容易敲错(如 i 写成 j),而编译时又检查不出来,有可能为了这个小小的错误而花费大量的查错时间。(5)除非必要,不要用数字或较奇怪的字符来定义标识符。(6)命名规范必须与所使用的系统风格保持一致,并在同一项目中统一。(7)在同一软件产品内,应规划好接口部分标识符(变量、结构、函数及常量)的命名,防止编译、链接时产生冲突。对接口部分的标识符应该有更严格限制,防止冲突。如可规定接口部分的变量与常量之前加

18、上“模块”标识等。(8)用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。下面是一些在软件中常用的反义词组。add / remove begin / end create / destroyinsert / delete first / last g et / releaseincrement / decrement put / getadd / delete lock / unlock open / closemin / max old / new start / stopnext / previous source / target show / hidesend / receiv

19、e source / destinationcut / paste up / down示例:int min_sum;int max_sum;12int add_user( BYTE *user_name );int delete_user( BYTE *user_name );(9)除了编译开关/ 头文件等特殊应用,应避免使用_EXAMPLE_TEST_ 之类以下划线开始和结尾的定义。3.3 变量名的命名规则(1)变量的命名规则要求用“匈牙利法则” 。即开头字母用变量的类型,其余部分用变量的英文意思、英文的缩写、中文全拼或中文全拼的缩写,要求单词的第一个字母应大写。即: 变量名=变量类型+变量

20、的英文意思(或英文缩写、中文全拼、中文全拼缩写)对非通用的变量,在定义时加入注释说明,变量定义尽量可能放在函数的开始处。见下表:bool 用 b 开头 bFlgint 用 i 开头 iCountshort int 用 n 开头 nStepCountlong int 用 l 开头 lSumchar 用 c 开头 cCountunsigned char 用 by 开头float 用 f 开头 fAvgdouble 用 d 开头 dDetaunsigned int(WORD) 用 w 开头 wCountunsigned long int(DWORD) 用 dw 开头 dwBroad字符串 用 s 开

21、头 sFileName用 0 结尾的字符串 用 sz 开头 szFileName(2)指针变量命名的基本原则为:对一重指针变量的基本原则为:“p”+变量类型前缀+命名,如一个 float*型应该表示为 pfStat。对二重指针变量的基本规则为:“pp”+变量类型前缀+命名。对三重指针变量的基本规则为:“ppp”+变量类型前缀+命名。(3)全局变量用 g_开头,如一个全局的长型变量定义为 g_lFailCount,即:变量名=g_+变量类型+变量的英文意思(或缩写)。此规则还可避免局部变量和全局变量同名而引起的问题。(4)静态变量用 s_开头,如一个静态的指针变量定义为 s_plPerv_Ins

22、t,即: 变量名=s_+变量类型+变量的英文意思(或缩写)(5)对枚举类型(enum)中的变量,要求用枚举变量或其缩写做前缀。并且要求用大写。如:enum cmEMDAYSEMDAYS_MONDAY;EMDAYS_TUESDAY;13;(6)对 struct、union 变量的命名要求定义的类型用大写。并要加上前缀,其内部变量的命名规则与变量命名规则一致。结构一般用 S 开头,如:struct ScmNPointint nX;/点的 X 位置int nY; /点的 Y 位置;联合体一般用 U 开头,如:union UcmLPointLONG lX;LONG lY;(7)对常量(包括错误的编码)

23、命名,要求常量名用大写,常量名用英文表达其意思。当需要由多个单词表示时,单词与单词之间必须采用连字符“_”连接。如:#define CM_FILE_NOT_FOUND CMMAKEHR(0X20B) 其中 CM 表示类别。(8)对 const 的变量要求在变量的命名规则前加入 c_,即:c_+变量命名规则;示例:const char* c_szFileName;3.4 函数的命名规范(1)函数的命名应该尽量用英文(或英文缩写、中文全拼、中文全拼缩写)表达出函数完成的功能函数名应准确描述函数的功能。遵循动宾结构的命名法则,函数名中动词在前,并在命名前加入函数的前缀,函数名的长度不得少于 8 个字

24、母。函数名首字大写,若包含有两个单词的每个单词首字母大写。如果是 OOP 方法,可以只有动词(名词是对象本身)。示例:LONG GetDeviceCount();void print_record( unsigned int rec_ind ) ;int input_record( void ) ;unsigned char get_current_color( void ) ;(2)避免使用无意义或含义不清的动词为函数命名。如使用 process、handle 等为函数命名,因为这些动词并没有说明要具体做什么。(3)必须使用函数原型声明。函数原型声明包括:引用外来函数及内部函数,外部引用必须

25、在右侧注明函数来源: 模块名及文件名;内部函数,只要注释其定义文件名和调用者在同一文件中(简单程序)时不需要注释。应确保每个函数声明中的参数的名称、类型和定义中的名称、类型一致。143.5 函数参数命名规范(1)参数名称的命名参照变量命名规范。(2)为了提高程序的运行效率,减少参数占用的堆栈,传递大结构的参数,一律采用指针或引用方式传递。(3)为了便于其他程序员识别某个指针参数是入口参数还是出口参数,同时便于编译器检查错误,应该在入口参数前加入 const 标志。如:cmCopyString(const CHAR * c_szSource, CHAR * szDest)3.6 文件名(包括动态

26、库、组件、控件、工程文件等)的命名规范文件名的命名要求表达出文件的内容,要求文件名的长度不得少于 5 个字母,严禁使用象 file1,myfile 之类的文件名。4 可读性4.1 避免使用默认的运算优先级注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级,可防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。示例:下列语句中的表达式word = (high b ) ? a : b ;改为如下就很清晰了。int max (int a, int b)return (a b) ? a : b);value = max (a, b);或改为如下。#define M

27、AX (a, b) (a) (b) ? (a) : (b)value = MAX (a, b);18当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替这样可以增加编程效率和程序的可读性。 示例:在某过程中较多引用 TheReceiveBufferFirstSocket.byDataPtr,则可以通过以下宏定义来代替:# define pSOCKDATA TheReceiveBufferFirstScoket.byDataPtr(3)防止把没有关联的语句放到一个函数中,防止函数或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程

28、中。随机内聚给函数或过程的维护、测试及以后的升级等造成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境。在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。示例:如下函数就是一种随机内聚。void Init_Var( void )Rect.length = 0;Rect.width = 0; /* 初始化矩形的长与宽 */Point.x = 10;Point

29、.y = 10; /* 初始化“点”的坐标 */矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。应如下分为两个函数:void Init_Rect( void )Rect.length = 0;Rect.width = 0; /* 初始化矩形的长与宽 */void Init_Point( void )Point.x = 10;Point.y = 10; /* 初始化“点”的坐标 */(4)如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题。若此段代码各语句之间有实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的函数。(5)减少函数本身或函数间的递归调用。

30、递归调用特别是函数间的递归调用(如 A-B-C-A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对19程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用,对于 safe-related 系统不能用递归,因为超出堆栈空间很危险。6.2 函数的返回值(1)对于函数的返回位置,尽量保持单一性,即一个函数尽量做到只有一个返回位置。(单入口单出口)。要求大家统一函数的返回值,所有的函数的返回值都将以编码的方式返回。例如编码定义如下:#define CM_POINT_IS_NULL CMMAKEHR(0X200):建议函数实现如下:LONG 函数

31、名(参数,)LONG lResult; /保持错误号lResult=CM_OK;/如果参数有错误则返回错误号if(参数=NULL)lResult=CM_POINT_IS_NULL;goto END;END:return lResult;(2)除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。(3)函数的返回值要清楚、明了,让使用者不容易忽视错误情况。函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。(4)函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出。带有内部“存储器”的函数的功能

32、可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样的函数既不易于理解又不利于测试和维护。在 C/C+语言中,函数的 static 局部变量是函数的内部存储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是 STATIC 的局部变量的地址作为返回值,若为 AUTO 类,则返回为错针。示例:如下函数,其返回值(即功能)是不可预测的。unsigned int integer_sum( unsigned int base )20unsigned int index;static unsigned int sum = 0; / 注意,是 static 类

33、型的。/ 若改为 auto 类型,则函数即变为可预测。for (index = 1; index MAX_GT_LENGTH)return GT_LENGTH_ERROR; / 忘了释放 gt_buf. / other program codefree( gt_buf );(3)防止内存操作越界。内存操作主要是指对数组、指针、内存地址等的操作。内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细小心。(4)编程时,要防止差 1 错误。此类错误一般是由于把“=”误写成“”等造成的,由此引起的后果,很多情况下是很严重的,所以编程时,一定要在这些地方小心。当编完程

34、序后,应对这些操作符进行彻底检查。24(5)要时刻注意易混淆的操作符。当编完程序后,应从头至尾检查一遍这些操作符,以防止拼写错误。形式相近的操作符最容易引起误用,如 C/C+中的“=”与“=” 、 “|”与“|”、 “Int32_t = j;j = sizeof(i = 1234);表达式 i = 1234 并没有执行,只是得到表达式类型 int 的 size。(9)逻辑操作符for (i=0;(i= 0) / 将出现下溢. / program code当 size 等于 0 时,再减 1 不会小于 0,而是 0xFF,故程序是一个死循环。应如下修改。char size; / 从 unsign

35、ed char 改为 charwhile (size- = 0). / program code(19)使用变量时要注意其边界值的情况。示例:如 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;(20)系统

36、应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救。9 宏(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)(2)将宏所定义的多条表达式放在大括号中。示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for 语句的书写稍不

37、符规范。#define INTI_RECT_VALUE( a, b )a = 0;b = 0;for (index = 0; index RECT_TOTAL_NUM; index+)INTI_RECT_VALUE( rect.a, rect.b );26正确的用法应为:#define INTI_RECT_VALUE( a, b )a = 0;b = 0;for (index = 0; index RECT_TOTAL_NUM; index+)INTI_RECT_VALUE( rectindex.a, rectindex.b );(3)使用宏时,不允许参数发生变化。示例:如下用法可能导致错误。#define SQUARE( a ) (a) * (a)int a = 5;int b;b = SQUARE( a+ ); / 结果:a = 7,即执行了两次增 1。正确的用法是:b = SQUARE( a );a+; / 结果:a = 6,即只执行了一次增 1。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报