1、华侨大学计算机学院华侨大学计算机学院C+/C编程规范编程规范 帮助程序员一次性编写出高质量的程序帮助程序员一次性编写出高质量的程序参考参考高质量高质量C/C+编程编程 第 2 页0.编程老手和高手的误区编程老手和高手的误区1.程序的版式程序的版式 2.命名规则命名规则3.表达式和基本语句表达式和基本语句4.函数设计函数设计5.内存管理内存管理6.C+/C试题与答案,代码检查表试题与答案,代码检查表第 3 页(1)真正的程序员真正的程序员没有进度表没有进度表,只有讨好领导的马屁精才有进度表,真,只有讨好领导的马屁精才有进度表,真正的程序员会让领导提心吊胆。正的程序员会让领导提心吊胆。(2)真正的
2、程序员真正的程序员不写使用说明书不写使用说明书,用户应当自己去猜想程序的功能。,用户应当自己去猜想程序的功能。(3)真正的程序员真正的程序员几乎不写代码的注释几乎不写代码的注释,如果注释很难写,它理所当然,如果注释很难写,它理所当然也很难读。也很难读。(4)真正的程序员真正的程序员不画流程图不画流程图,原始人和文盲才会干这事。,原始人和文盲才会干这事。(5)真正的程序员真正的程序员不看参考手册不看参考手册,新手和胆小鬼才会看。,新手和胆小鬼才会看。(6)真正的程序员真正的程序员不写文档也不需要文档不写文档也不需要文档,只有看不懂程序的笨蛋才用,只有看不懂程序的笨蛋才用文档。文档。(7)真正的程
3、序员真正的程序员认为自己比用户更明白用户需要什么认为自己比用户更明白用户需要什么。(8)真正的程序员真正的程序员不接受团队开发不接受团队开发的理念,除非他自己是头头。的理念,除非他自己是头头。(9)真正的程序员的程序不会在第一次就正确运行,但是他们真正的程序员的程序不会在第一次就正确运行,但是他们愿意守着愿意守着机器进行若干个机器进行若干个30小时的调试改错小时的调试改错。(10)真正的程序员不会在上午真正的程序员不会在上午9:00到下午到下午5:00之间工作,如果你看到他之间工作,如果你看到他在在上午上午9:00工作,这表明他从昨晚一直干到现在。工作,这表明他从昨晚一直干到现在。第 4 页面
4、试题:面试题:strcpy的代码。的代码。一位一位“高手高手”面对微软资深工程师的面试面对微软资深工程师的面试(1)编程风格;)编程风格;(2)出错处理;)出错处理;(3)算法复杂度分析)算法复杂度分析(用于提高性能用于提高性能)第 5 页1)文件结构文件结构 版权和版本版权和版本头文件的结构头文件的结构头文件的作用 目录结构目录结构 includesrc2)版式版式第 6 页1.1 版权和版本的声明版权和版本的声明位于头文件和定义文件的开头位于头文件和定义文件的开头(1)版权信息。)版权信息。(2)文件名称,标识符,摘要)文件名称,标识符,摘要(3)当前版本号,作者)当前版本号,作者 /修改
5、者,完成日期。修改者,完成日期。(4)版本历史信息。)版本历史信息。/*Copyright(c)2001,Copyright(c)2001,上海贝尔有限公司上海贝尔有限公司*All rights reserved.All rights reserved.*文件名称:文件名称:filename.hfilename.h*文件标识:见配置管理计划书文件标识:见配置管理计划书*摘摘 要:简要描述本文件的内容要:简要描述本文件的内容*当前版本:当前版本:1.11.1*作作 者:输入作者(或修改者)名字者:输入作者(或修改者)名字*完成日期:完成日期:20012001年年7 7月月2020日日*取代版本取
6、代版本:1.0*原作者原作者 :输入原作者(或修改者)名:输入原作者(或修改者)名字字*完成日期:完成日期:20012001年年5 5月月1010日日*/第 7 页/版权和版本声明见示例版权和版本声明见示例1-1,此处省略。,此处省略。#ifndef GRAPHICS_H/防止防止raphics.h被重复引用被重复引用#define GRAPHICS_H#include /引用标准库的头文件引用标准库的头文件#include“myheader.h”/引用非标准库的头文引用非标准库的头文件件void Function1();/全局函数声明全局函数声明class Box /类结构声明类结构声明;#
7、endif(1)版权和版本)版权和版本(2)预处理块。)预处理块。(3)函数和类声明)函数和类声明#include#include“filename.h”的区别/不提倡使用全局变量,少定义 正确使用正确使用include,ifndef/define/endif第 8 页通过头文件来调用库功能。通过头文件来调用库功能。出于安全的考虑:出于安全的考虑:n在很多场合,源代码不便(或不准)向用户公布,只要在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。向用户提供头文件和二进制的库即可。n用户只需要按照头文件中的接口声明来调用户只需要按照头文件中的接口声明来调用库功能,
8、而不必关心接口怎么实现的用库功能,而不必关心接口怎么实现的。编。编译器会从库中提取相应的代码。译器会从库中提取相应的代码。第 9 页程序的版式程序的版式程序的书法程序的书法一目了然一目了然 1)适时加空行:适时加空行:不浪费内存不浪费内存别不舍得用!别不舍得用!每个类声明之后、每个函数定义结束之后加空行每个类声明之后、每个函数定义结束之后加空行 2)代码:一行一句代码:一行一句易读、便于设断点测试 例3)用空格分隔用空格分隔 n关键字后、二元运算符后要留空格关键字后、二元运算符后要留空格 n一元运算符不留空一元运算符不留空n 函数名后函数名后()前后不用空格前后不用空格 例4)注意注意 对齐对
9、齐 例5)长行拆分长行拆分 一行控制在一行控制在70至至80个字符以内个字符以内,便于观看、打印,便于观看、打印6)注释的使用)注释的使用 例:例:7)类的定义类的定义 先操作再数据先操作再数据 例例第 10 页int width;/宽度宽度int height;/高度高度int depth;/深度深度int width,height,depth;x=a+b;y=c+d;z=e+f;X a+b;y=c+d;z=e+f;if(width height)dosomething();if(width=2000)/良好的风格良好的风格if(year=2000)/不良的风格不良的风格if(a=b)&(c
10、=b&c=d)/不良的风格不良的风格for(i=0;i10;i+)/良好的风格良好的风格for(i=0;i10;i+)/不良的风格不良的风格for(i=0;i 10;i+)/过多的空格过多的空格x=a b?a:b;/良好的风格良好的风格x=a=b&c d&c+f=”或“=-EPSINON)&(x=EPSINON)/其中其中EPSINON是允许的误差(即精度)是允许的误差(即精度)第 29 页应当将指针变量用应当将指针变量用“=”或或“!=”与与NULL比较。比较。指针变量的零值是指针变量的零值是“空空”(记为(记为NULL)。尽管)。尽管NULL的值的值与与0相同,但两者意义不同。假设指针变量
11、的名字为相同,但两者意义不同。假设指针变量的名字为pif(p=NULL)或或if(p!=NULL)/p与与NULL显式比较,强显式比较,强调调p是指针变量是指针变量不要写成不要写成if(p=0)/容易让人误解容易让人误解p是整型变量是整型变量if(p!=0)或或 if(p)/容易让人误解容易让人误解p是布尔变量是布尔变量 if(!p)第 30 页多重循环,若可能,应多重循环,若可能,应长循环在内,短循环在外长循环在内,短循环在外n以减少以减少CPU跨切循环层的次数。跨切循环层的次数。若循环体内有逻辑判断,且循环次数很大,宜将若循环体内有逻辑判断,且循环次数很大,宜将逻辑判断移到循环体外逻辑判断
12、移到循环体外 循环控制变量采用循环控制变量采用“半开半闭区间半开半闭区间”写法。写法。即即:x介于介于0,N)for(int x=0;xN;x+)而不是 x介于0,N-1 for(int x=0;x=N-1;x+)第 31 页函数体的函数体的“入口处入口处”和和“出口处出口处”把关把关 4.1 参数4.2返回值第 32 页参数写完整、命名恰当、顺序合理参数写完整、命名恰当、顺序合理StringCopy(char*str1,char*str2)类型类型和和参数名参数名都要写都要写参为指针,若仅传入数据,则加参为指针,若仅传入数据,则加const参数个数参数个数55个个第 33 页返回值类型别省略
13、。返回值类型别省略。n正常值正常值输出参数输出参数获得获得n错误标志错误标志用用returnreturn返回返回 为增加灵活性支持链式表达,可附为增加灵活性支持链式表达,可附加返回值加返回值 char*strcpy(char*strDest,const char*strSrc);明确说明用于出错处理的返回值明确说明用于出错处理的返回值n让使用者不容易忽视或误解错误情况让使用者不容易忽视或误解错误情况第 34 页函数的功能要单一函数的功能要单一函数体函数体 50行行检查所有进入函数体的变量的有效性,检查所有进入函数体的变量的有效性,1 1)输入参数)输入参数2 2)通过其他途径进入函数体内的变量
14、)通过其他途径进入函数体内的变量n如全局变量、文件句柄等。第 35 页使用断言捕捉不应该发生的非法情况。使用断言捕捉不应该发生的非法情况。如:函数入口处,用断言检查参数的有效性。如:函数入口处,用断言检查参数的有效性。void *memcpy(void*pvTo,const void*pvFrom,size_t size)assert(pvTo!=NULL)&(pvFrom!=NULL);/使用断言使用断言 第 36 页5.2 常见的内存错误常见的内存错误&对策对策5.4 free 和和 delete 把指针怎么啦?把指针怎么啦?5.5 动态内存会被自动释放吗?动态内存会被自动释放吗?5.6
15、杜绝野指针杜绝野指针5.7 有了有了malloc/free 为什么还要为什么还要new/delete5.8 内存耗尽怎么办?内存耗尽怎么办?第 37 页1静态存储区域静态存储区域n内存在程序编译的时候就已分配好,这块内存在程序的整个运行期间都存在。n如全局变量,static变量。2.栈栈n函数执行时创建,函数结束时自动被释放n栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。n如:函数内局部变量、参数的存储单元3.堆(动态内存分配)堆(动态内存分配)nMalloc/free或new/deleten运行时用malloc或new申请任意多少的内存,程序员自己负责在何时用fre
16、e或delete释放内存。动态内存的生存期由我们决定,使用灵活,但问题也最多。第 38 页n内存分配未成功,却使用了它。内存分配未成功,却使用了它。n内存分配虽然成功,但尚未初始化就引用它。内存分配虽然成功,但尚未初始化就引用它。n内存分配成功并且已经初始化,但操作越过了内存的边界。内存分配成功并且已经初始化,但操作越过了内存的边界。n忘记了释放内存,造成内存泄漏。忘记了释放内存,造成内存泄漏。n释放了内存却继续使用它释放了内存却继续使用它 内存内存分配成功分配成功了?了?未成功未成功那你敢就用它那你敢就用它?申请的内存申请的内存初始化初始化了么?了么?成功未成功未成功操作操作越界越界了么?了
17、么?是的是的成了野指针成了野指针内存你内存你释放释放了么?了么?我忘了我忘了内存泄漏,资源崩溃内存泄漏,资源崩溃释放释放了,你还用!了,你还用!我忘了我忘了第 39 页内存内存分配成功分配成功了?了?申请的内存申请的内存初始化初始化了么?了么?避免避免越界越界内存你内存你释放释放了么?了么?释放释放了,你还用!了,你还用!n用用malloc或或new申请内存之后,应该申请内存之后,应该立即检查指立即检查指针值是否为针值是否为NULL。防止使用指针值为防止使用指针值为NULL内存内存n别忘记为数组和动态内存赋初值别忘记为数组和动态内存赋初值。防止将。防止将未被初始化的内存作为有值使用。未被初始化
18、的内存作为有值使用。n避免数组或指针的下标越界,特别要当心避免数组或指针的下标越界,特别要当心发生发生“多多1”或者或者“少少1”操作。操作。n动态内存的申请与动态内存的申请与释放释放必须配对,防止内必须配对,防止内存泄漏。存泄漏。n用用free或或delete释放了内存之后,立即将指释放了内存之后,立即将指针设置为针设置为NULL,防产生防产生“野指针野指针”。第 40 页它们只是把指针所指的内存给释放掉,但并没有把它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。指针本身干掉。n指针指针p被被free以后其地址仍然不变(非以后其地址仍然不变(非NULL),),只是该只是该地址对应的
19、内存是垃圾,地址对应的内存是垃圾,p成了成了“野指针野指针”。如果此时不如果此时不把把p设置为设置为NULL,会让人误以为会让人误以为p是个合法的指针是个合法的指针。n如果程序比较长,我们有时记不住如果程序比较长,我们有时记不住p所指的内存是否已经所指的内存是否已经被释放,在继续使用被释放,在继续使用p之前,通常会用语句之前,通常会用语句if(p!=NULL)进行防错处理。很遗憾,此时进行防错处理。很遗憾,此时if语句起不到防错语句起不到防错作用,因为即便作用,因为即便p不是不是NULL指针,它也不指向合法的内指针,它也不指向合法的内存块。存块。“野指针野指针”示例示例第 41 页示例示例n指
20、针消亡了,并不表示它所指的内存会被指针消亡了,并不表示它所指的内存会被自动释放。自动释放。n内存被释放了,并不表示指针会消亡或者内存被释放了,并不表示指针会消亡或者成了成了NULLNULL指针。指针。第 42 页“野指针野指针”不是不是NULL指针,是指向指针,是指向“垃圾垃圾”内存内存的指针。的指针。n人们一般不会错用人们一般不会错用NULL指针,因为用指针,因为用if语句很容易判断。语句很容易判断。但是但是“野指针野指针”是很危险的,是很危险的,if语句对它不起作用。语句对它不起作用。“野指针野指针”的成因的成因1)指针变量没有被初始化)指针变量没有被初始化。n任何指针变量刚被创建时不会自
21、动成为NULL指针,它的默认值是随机的,它会乱指一气。2)指针)指针p被被free或者或者delete之后,没有置为之后,没有置为NULLn让人误以为p是个合法的指针。3)指针越界)指针越界n操作超越了变量的作用范围。这种情况让人防不胜防 第 43 页nMalloc/free 是库函数,是库函数,new/delete是运算符。是运算符。n光用光用maloc/free无法满足动态对象的要求。对象在创建的同时要自无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是
22、运算符,不在编译器控制权限之内,不是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于能够把执行构造函数和析构函数的任务强加于malloc/free。因此因此C+语言需要一个能完成动态内存分配和初始化工作的运算符语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符以及一个能完成清理与释放内存工作的运算符delete。Malloc/freenew/delete性质性质是是C库函数库函数是是C+运算符运算符能否自动调用对象的能否自动调用对象的构造函数和析构函数构造函数和析构函数完成复杂对象的初始化完成复杂对象的初始化不能不能可以可以第 44 页建议:建议:n必须养成必须养成“使用调试器逐步跟踪程序使用调试器逐步跟踪程序”的的习惯,只有这样才能发现问题的本质。习惯,只有这样才能发现问题的本质。第 45 页详见高质量程序设计指南详见高质量程序设计指南C+/C语言语言(第二版)(第二版)的附录,电子工业出版社,的附录,电子工业出版社,2003年年6月月