上章回顾哈希函数的构造方法有那些哈希表中处理冲突的方法有那些高质量编程规范第八章预习检查C语言要经历哪几个编译过程如何申请链表单元,及释放链表单元实现单链表插入的基本语法简述一下快速排序基本理论要点课程目标本章概述阐述如何进行高质量的编程,以及注意事项本章目标了解高质量编程注意的方方面面从代码风格,算法,方便调试,性能等。重点内存分配与释放,悬挂指针本章结构程序员的态度高质量编程规范微观上高质量宏观上高质量8.1程序员的态度程序员的弱点不太愿意测试自己的代码不愿意REVIEW团队队员的代码程序员重点保证自己的代码没有BUG来8.1程序员的态度程序员自身可以在程序生成的流程详细设计编写代码单元测试功能测试代码REVIEW8.1.1编码的风格版权和版本的申明头文件的结构定义文件的结构头文件的作用目录结构命名规则注释规则/*Copyright(c)2001,上海贝尔有限公司网络应用事业部*Allrightsreserved.**文件名称:filename.h*文件标识:见配置管理计划书*摘要:简要描述本文件的内容**当前版本:1.1*作者:输入作者(或修改者)名字*完成日期:2001年7月20日**取代版本:1.0*原作者:输入原作者(或修改者)名字*完成日期:2001年5月10日8.1.1.1版权和版本的申明版权和版本的声明位于头文件和定义文件的开头,主要内容有:(1)版权信息。(2)文件名称,标识符,摘要。(3)当前版本号,作者/修改者,完成日期。(4)版本历史信息。范例8.1.1.2头文件的结构头文件由三部分内容组成:头文件开头处的版权和版本声明。预处理块。函数和类结构声明等。范例为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块。用#includefilename.h格式来引用标准库的头文件(编译器将从标准库目录开始搜索)。用#include“filename.h”格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。头文件中只存放“声明”而不存放“定义”不提倡使用全局变量,尽量不要在头文件中出现象externintvalue这类声明。8.1.1.3定义文件的结构定义文件有三部分内容:定义文件开头处的版权和版本声明对一些头文件的引用程序的实现体(包括数据和代码)范例//版权和版本声明#include“graphics.h”//引用头文件…//全局函数的实现体voidFunction1(…){…}8.1.1.4头文件的作用通过头文件来调用库功能头文件能加强类型安全检查8.1.1.5目录结构特点:便于维护通常应将头文件和定义文件分别保存于不同的目录加强信息隐藏:如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”范例:Network工程建立三个目录source:存放工程源文件,如:server.cClient.cInclude:存放工程头文件,如:server.hClient.hLib:存放工程库文件,如:tipr.sostdio.so8.1.1.6命名规则主要思想:在变量和函数名中加入前缀以增进人们对程序的理解具体规则:标识符应当直观且可以拼读,可望文知意,不必进行“解码”。标识符的长度应当符合“min-length&&max-information”原则命名规则尽量与所采用的操作系统或开发工具的风格保持一致程序中不要出现仅靠大小写区分的相似的标识符程序中不要出现标识符完全相同的局部变量和全局变量变量的名字应当使用“名词”或者“形容词+名词”用正确的反义词组命名具有互斥意义的变量或相反动作的函数等8.1.2程序的版式空行代码行代码行内的空格代码对齐长行拆分修饰符的位置注释8.1.2.1空行空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存空行规则每个函数定义结束之后都要加空行在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。8.1.2.2代码行规则一行代码只做一件事情如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}。尽可能在定义变量的同时初始化该变量(就近原则)8.1.2.3代码行内的空格关键字之后要留空格函数名之后不要留空格,紧跟左括号‘(’,以与关键字区别‘(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格‘,’之后要留空格二元操作符的前后应当加空格。一元操作符前后不加空格。象“[]”、“.”、“-”这类操作符前后不加空格。8.1.2.4对齐和拆分规则对齐规则程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐{}之内的代码块在‘{’右边数格处左对齐。长行拆分规则代码行最大长度宜控制在70至80个字符以内长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读8.1.2.5长行拆分规则代码行最大长度宜控制在70至80个字符以内长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)例:if((very_longer_variable1=very_longer_variable12)&&(very_longer_variable3=very_longer_variable14)&&(very_longer_variable5=very_longer_variable16)){dosomething();}8.1.2.6修饰符的位置修饰符*和&修饰符紧靠变量名例如:char*name;int*x,y;//此处y不会被误解为指针x?y?8.1.2.7注释C语言的注释符为“/*…*/”行注释一般采用“//…”注释通常用于版本、版权声明;函数接口说明;重要的代码行或段落提示。8.1.2.7注释注释规则注释是对代码的“提示”,而不是文档如果代码本来就是清楚的,则不必加注释。边写代码边注释注释应当准确、易懂,防止注释有二义性尽量避免在注释中使用缩写,特别是不常用缩写。注释的位置应与被描述的代码相邻当代码比较长,应当在一些段落的结束处加注释8.1.2.7注释注释实例/**函数介绍:*输入参数:*输出参数:*返回值:*/voidFunction(floatx,floaty,floatz){…}阶段小节版权的申明和头文件的结构及作用命名的规则代码的版式规则空行对齐拆分代码的注释8.2微观上的高质量程序的健壮性防止内存泄漏编程的优化8.2.1程序的健壮性使用断言复合表达式If语句使用const提高函数的健壮性8.2.1.1使用断言程序一般分为Debug版本和Release版本断言assert是仅在Debug版本起作用的宏断言优势:跟踪程序运行,帮助调试输出错误原因可以自定义8.2.1.1使用断言使用断言规则:使用断言捕捉不应该发生的非法情况在函数的入口处,使用断言检查参数的有效性(合法性)。一旦确定了的假定,就要使用断言对假定进行检查。如果“不可能发生”的事情的确发生了,则要使用断言进行报警8.2.1.2复合表达式复合表达式例子a=b=c=0优势:书写简洁提高编译效率8.2.1.2复合表达式复合表达式使用规则不要编写太复杂的复合表达式。例子:i=a=b&&cd&&c+f=g+h;不要有多用途的复合表达式d=(a=b+c)+r;不要把程序中的复合表达式与“真正的数学表达式”混淆if(abc)与if((ab)&&(bc))8.2.1.3If语句布尔变量与零值比较不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。例子:假设布尔变量名字为flag,它与零值比较的标准if语句如下:if(flag)//表示flag为真if(!flag)//表示flag为假其它的用法都属于不良风格,例如:if(flag==TRUE)if(flag==1)if(flag==FALSE)if(flag==0)8.2.1.3If语句整型变量与零值比较整型变量用“==”或“!=”直接与0比较例子:假设整型变量的名字为value,它与零值比较的标准if语句如下:if(value==0)if(value!=0)不可模仿布尔变量的风格而写成if(value)//会让人误解value是布尔变量if(!value)8.2.1.3If语句浮点变量与零值比较不可将浮点变量用“==”或“!=”与任何数字比较设法转化成“=”或“=”形式例子:假设浮点变量的名字为x,应当将if(x==0.0)//隐含错误的比较转化为if((x=-EPSINON)&&(x=EPSINON))其中EPSINON是允许的误差(即精度)。8.2.1.3If语句指针变量与零值比较指针变量用“==”或“!=”与NULL比较例子:与零值比较的标准if语句如下:if(p==NULL)//p与NULL显式比较,强调p是指针变量if(p!=NULL)不要写成if(p==0)//容易让人误解p是整型变量if(p!=0)或者if(p)//容易让人误解p是布尔变量if(!p)8.2.1.4使用const提高函数的健壮性const用法:定义常量修饰函数的参数修饰函数的返回值修饰函数的定义体8.2.1.4.1用const修饰函数的参数const只能修饰输入参数特点如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针,起到保护作用例:voidStringCopy(char*strDestination,constchar*strSource);如果输入参数采用“引用传递”,可以避免修改参数值的值传递voidFunc(constA&a)8.2.1.4.2用const修饰函数的返回值如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)内容不能被修改例如函数constchar*GetString(void);如下语句将出现编译错误:char*str=GetString();正确的用法是constchar*str=GetString();8.3.1防止内存泄漏内存分配方式malloc/free的使用要点常见的内存错误及其对策引用与指针的比较指针与数组的对比指针参数是如何传递内存的动态内存自动释放杜绝“野指针”8.3.1.1内存分配方式内存分配方式有三种:从静态存储区域分配在栈上创建从堆上分配,亦称动态内存分配malloc或newfree或delete8.3.1.2malloc/free的使用要点malloc语法:void*malloc(size_tsize);作用:申请一块长度为length的整数类型的内存例子:int*p=(int*)malloc(sizeof(int)*length)Free语法:voidfree(void*memblock)作用:释放内存例子:free(p)如果p是NULL指针,那么free对p无论操作多少次都不会出问题如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。8.3.1.3常见的内存错误及其对策常见的内存错误内存分配未成功,却使用了它内存分配虽然成功,但是尚未初始化就引用它内存分配成功并且已经初始化,但操作越过了内存的边界忘记了释放内存,造成内存泄露释放了内存却继续使用它8.3.1.3常见的内存错误及其对策内存管理的规则用malloc之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。动态内存的申请与释放必须配对,防止内存泄漏。用free释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。8.3.1.5指针与数组的对比差别数组:要么在静态存储区被创建(如全局数组),要么在栈上被创建数组名对应着(而不是指向)一块内存其地址与容量在生命期内保持不