CC++高质量编程综合指南_第1页
CC++高质量编程综合指南_第2页
CC++高质量编程综合指南_第3页
CC++高质量编程综合指南_第4页
CC++高质量编程综合指南_第5页
已阅读5页,还剩189页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 HYPERLINK 高质量C+/C编程指南南文件状态 草稿稿文件 正式文文件 更改改正式文件文件标识:当前版本:1.0作 者:林锐 博士完成日期:2001年7月月24日前 言软件质量是被被大多数程序序员挂在嘴上上而不是放在在心上的东西西!除了完全外行行和真正的编编程高手外,初初读本书,你你最先的感受受将是惊慌:“哇!我以前捏捏造的C+/C程序怎怎么会有那么么多的毛病?”别难过,作者者只不过比你你早几年、多多几次惊慌而而已。请花一两个小小时认真阅读读这本百页经经书,你将会会获益匪浅,这这是前面N-1个读者的的建议。一、编程老手与与高手的误区区自从计算机问世世以来,程序序设计就成了了令人羡慕的的

2、职业,程序序员在受人宠宠爱之后容易易发展成为毛毛病特多却常常能自我臭美美的群体。如今在Inteernet上上流传的“真正”的程序员据据说是这样的的:真正的程序员没没有进度表,只只有讨好领导导的马屁精才才有进度表,真真正的程序员员会让领导提提心吊胆。真正的程序员不不写使用说明明书,用户应应当自己去猜猜想程序的功功能。真正的程序员几几乎不写代码码的注释,如如果注释很难难写,它理所所当然也很难难读。真正的程序员不不画流程图,原原始人和文盲盲才会干这事事。真正的程序员不不看参考手册册,新手和胆胆小鬼才会看看。真正的程序员不不写文档也不不需要文档,只只有看不懂程程序的笨蛋才才用文档。真正的程序员认认为自

3、己比用用户更明白用用户需要什么么。真正的程序员不不接受团队开开发的理念,除除非他自己是是头头。真正的程序员的的程序不会在在第一次就正正确运行,但但是他们愿意意守着机器进进行若干个330小时的调调试改错。真正的程序员不不会在上午99:00到下午午5:00之间工工作,如果你你看到他在上上午9:00工作,这这表明他从昨昨晚一直干到到现在。具备上述特征越越多,越显得得水平高,资资格老。所以以别奇怪,程程序员的很多多缺点竟然可可以被当作优优点来欣赏。就就象在武侠小小说中,那些些独来独往、不不受约束且带带点邪气的高高手最令人崇崇拜。我曾经经也这样信奉奉,并且希望望自己成为那那样的“真正”的程序员,结结果没

4、有得到到好下场。我从读大学到博博士毕业十年年来一直勤奋奋好学,累计计编写了数十十万行C+/C代码。有有这样的苦劳劳和疲劳,我我应该称得上上是编程老手手了吧?我开发的软件都都与科研相关关(集成电路路CAD和33D图形学领领域),动辄辄数万行程序序,技术复杂杂,难度颇高高。这些软件件频频获奖,有有一个软件获获得首届中国国大学生电脑脑大赛软件展展示一等奖。在在1995年开开发的一套图图形软件库到到2000年年还有人买。罗罗列出这些“业绩”,可以说明明我算得上是是编程高手了了吧?可惜这种个人感感觉不等于事事实。读博期间我曾用用一年时间开开发了一个近近10万行CC+代码的的3D图形软软件产品,我我内心得

5、意表表面谦虚地向向一位真正的的软件高手请请教。他虽然然从未涉足过过3D图形领领域,却在几几十分钟内指指出该软件多多处重大设计计错误。让人人感觉那套软软件是用纸糊糊的华丽衣服服,扯一下掉掉一块,戳一一下破个洞。我我目瞪口呆地地意识到这套套软件毫无实实用价值,一一年的心血白白化了,并且且害死了自己己的软件公司司。人的顿悟通常发发生在最心痛痛的时刻,在在沮丧和心痛痛之后,我作作了深刻反省省,“面壁”半年,重新新温习软件设设计的基础知知识。补修“内功”之后,又觉觉得腰板硬了了起来。博士士毕业前半年年,我曾到微微软中国研究究院找工作,接接受微软公司司一位资深软软件工程师的的面试。他让让我写函数sstrc

6、pyy的代码。太容易了吧?错!这么一个小不点点的函数,他他从三个方面面考查:(1)编程风格格;(2)出错处理理;(3)算法复杂杂度分析(用用于提高性能能)。在大学里从来没没有人如此严严格地考查过过我的程序。我我化了半个小小时,修改了了数次,他还还不尽满意,让让我回家好好好琢磨。我精精神抖擞地进进“考场”,大汗淋漓漓地出“考场”。这“高手”当得也太窝窝囊了。我又又好好地反省省了一次。我把反省后的心心得体会写成成文章放在网网上传阅,引引起了不少软软件开发人员员的共鸣。我我因此有幸和和国产大型IIT企业如华华为、上海贝贝尔、中兴等等公司的同志志们广泛交流流。大家认为为提高质量与与生产率是软软件工程要

7、解解决的核心问问题。高质量量程序设计是是非常重要的的环节,毕竟竟软件是靠编编程来实现的的。我们心目中的老老手们和高手手们能否编写写出高质量的的程序来?不见得都能!就我的经历与阅阅历来看,国国内大学的计计算机教育压压根就没有灌灌输高质量程程序设计的观观念,教师们们和学生们也也很少自觉关关心软件的质质量。勤奋好好学的程序员员长期在低质质量的程序堆堆中滚爬,吃吃尽苦头之后后才有一些心心得体会,长长进极慢,我我就是一例。现在国内IT企企业拥有学士士、硕士、博博士文凭的软软件开发人员员比比皆是,但但他们在接受受大学教育时时就“先天不足”,岂能一到到企业就突然然实现质的飞飞跃。试问有有多少软件开开发人员对

8、正正确性、健壮壮性、可靠性性、效率、易易用性、可读读性(可理解解性)、可扩扩展性、可复复用性、兼容容性、可移植植性等质量属属性了如指掌掌?并且能在在实践中运用用自如?。“高质量”可不是干活活小心点就能能实现的!我们有充分的理理由疑虑:(1)编程老手手可能会长期期用隐含错误误的方式编程程(习惯成自自然),发现现毛病后都不不愿相信那是是真的!(2)编程高手手可以在某一一领域写出极极有水平的代代码,但未必必能从全局把把握软件质量量的方方面面面。事实证明如此此。我到上海海贝尔工作一一年来,陆续续面试或测试试过近百名“新”“老”程序员的编编程技能,质质量合格率大大约是10。很少有人人能够写出完完全符合质

9、量量要求的iff语句,很多多程序员对指指针、内存管管理一知半解解,。领导们不敢相信信这是真的。我我做过现场试试验:有一次次部门新进114名硕士生生,在开欢迎迎会之前对他他们进行“C+/C编程技技能”摸底考试。我我问大家试题题难不难?所所有的人都回回答不难。结结果没有一个个人及格,有有半数人得零零分。竞争对对手公司的朋朋友们也做过过试验,同样样一败涂地。真的不是我“心心狠手辣”或者要求过过高,而是很很多软件开发发人员对自己己的要求不够够高。要知道华为、上上海贝尔、中中兴等公司的的员工素质在在国内IT企业中是是比较前列的的,倘若他们们的编程质量量都如此差的的话,我们怎怎么敢期望中中小公司拿出出高质

10、量的软软件呢?连程程序都编不好好,还谈什么么振兴民族软软件产业,岂岂不胡扯。我打算定义编程程老手和编程程高手,请您您别见笑。定义1:能长期期稳定地编写写出高质量程程序的程序员员称为编程老老手。定义2:能长期期稳定地编写写出高难度、高高质量程序的的程序员称为为编程高手。根据上述定义,马马上得到第一一推论:我既既不是高手也也算不上是老老手。在写此书前,我我阅读了不少少程序设计方方面的英文著著作,越看越越羞惭。因为为发现自己连连编程基本技技能都未能全全面掌握,顶顶多算是二流流水平,还好好意思谈什么么老手和高手手。希望和我我一样在国内内土生土长的的程序员朋友友们能够做到到:(1)知错就改改;(2)经常

11、温故故而知新;(3)坚持学习习,天天向上上。二、本书导读首先请做附录录B的C+/CC试题(不要看看答案),考考查自己的编编程质量究竟竟如何。然后后参照答案严严格打分。(1)如果你只只得了几十分分,请不要声声张,也不要要太难过。编编程质量差往往往是由于不不良习惯造成成的,与人的的智力、能力力没有多大关关系,还是有有药可救的。成成绩越差,可可以进步的空空间就越大,中中国不就是在在落后中赶超超发达资本主主义国家吗?只要你能下下决心改掉不不良的编程习习惯,第二次次考试就能及及格了。(2)如果你考考及格了,表表明你的技术术基础不错,希希望你能虚心心学习、不断断进步。如果果你还没有找找到合适的工工作单位,

12、不不妨到上海贝贝尔试一试。(3)如果你考考出85分以以上的好成绩绩,你有义务务和资格为你你所在的团队队作“C+/CC编程”培训。希望你你能和我们多多多交流、相相互促进。半半年前我曾经经发现一颗好好苗子,就把把他挖到我们们小组来。(4)如果你在在没有任何提提示的情况下下考了满分,希希望你能收我我做你的徒弟弟。编程考试结束束后,请阅读读本书的正文文。本书第一章至至第六章主要要论述C+/C编程风风格。难度不不高,但是细细节比较多。别别小看了,提提高质量就是是要从这些点点点滴滴做起起。世上不存存在最好的编编程风格,一一切因需求而而定。团队开开发讲究风格格一致,如果果制定了大家家认可的编程程风格,那么么

13、所有组员都都要遵守。如如果读者觉得得本书的编程程风格比较合合你的工作,那那么就采用它它,不要只看看不做。人在在小时候说话话发音不准,写写字潦草,如如果不改正,总总有后悔的时时候。编程也也是同样道理理。第七章至第十十一章是专题题论述,技术术难度比较高高,看书时要要积极思考。特特别是第七章章“内存管理”,读了并不不表示懂了,懂懂了并不表示示就能正确使使用。有一位位同事看了第第七章后觉得得“野指针”写得不错,与与我切磋了一一把。可是过过了两周,他他告诉我,他他忙了两天追追查出一个BBug,想不不到又是“野指针”出问题,只只好重读第七七章。光看本书对提高高编程质量是是有限的,建建议大家阅读读本书的参考

14、考文献,那些些都是经典名名著。如果你的编程程质量已经过过关了,不要要就此满足。如如果你想成为为优秀的软件件开发人员,建建议你阅读并并按照CMMMI规范做事事,让自己的的综合水平上上升一个台阶阶。上海贝尔尔的员工可以以向网络应用用事业部软件件工程研究小小组索取CMMMI有关资资料,最好能能参加培训。三、版权声明本书的大部分分内容取材于于作者一年前前的书籍手稿稿(尚未出版版),现整理理汇编成为上上海贝尔网络络应用事业部部的一个规范范化文件,同同时作为培训训教材。由于C+/C编程是众众所周知的技技术,没有秘秘密可言。编编程的好经验验应该大家共共享,我们自自己也是这么么学来的。作作者愿意公开开本书的电

15、子子文档。版权声明如下下:(1)读者可以以任意拷贝、修修改本书的内内容,但不可可以篡改作者者及所属单位位。(2)未经作者者许可,不得得出版或大量量印发本书。(3)如果竞争争对手公司的的员工得到本本书,请勿公公开使用,以以免发生纠纷纷。预计到20002年7月,我我们将建立切切合中国国情情的CMMII 3级解决决方案。届时时,包括本书书在内的约11000页规规范将严格受受控。欢迎读者对本本书提出批评评建议。林锐,20011年7月第1章 文件结结构每个C+/CC程序通常分分为两个文件件。一个文件件用于保存程程序的声明(ddeclarrationn),称为头头文件。另一一个文件用于于保存程序的的实现(

16、immplemeentatiion),称称为定义(ddefiniition)文文件。C+/C程序序的头文件以以“.h”为后缀,CC程序的定义义文件以“.c”为后缀,CC+程序的的定义文件通通常以“.cpp”为后缀(也也有一些系统统以“.cc”或“.cxx”为后缀)。1.1 版权和和版本的声明明版权和版本的声声明位于头文文件和定义文文件的开头(参参见示例1-1),主要要内容有:(1)版权信息息。(2)文件名称称,标识符,摘摘要。(3)当前版本本号,作者/修改者,完完成日期。(4)版本历史史信息。/* Copyrright (c) 22001,上海贝尔有有限公司网络络应用事业部部* All rri

17、ghtss reseerved.* * 文件名称:* 文件标识:见配置管理计计划书* 摘 要:简要描描述本文件的的内容* * 当前版本:1.1* 作 者:输入作者者(或修改者者)名字* 完成日期:2001年年7月20日日* 取代版本:1.0 * 原作者 :输入原作作者(或修改改者)名字* 完成日期:2001年年5月10日日*/示例1-1 版版权和版本的的声明1.2 头文件件的结构头文件由三部分分内容组成:(1)头文件开开头处的版权权和版本声明明(参见示例例1-1)。(2)预处理块块。(3)函数和类类结构声明等等。假设头文件名称称为 grapphics.h,头文件件的结构参见见示例1-22。【规

18、则1-2-1】为了防止头头文件被重复复引用,应当当用ifnddef/deefine/endiff结构产生预预处理块。【规则1-2-2】用 #incllude 格式来引引用标准库的的头文件(编编译器将从标标准库目录开开始搜索)。【规则1-2-3】用 #incllude “” 格式来引用用非标准库的的头文件(编编译器将从用用户的工作目目录开始搜索索)。【建议1-2-1】头文件中只只存放“声明”而不存放“定义”在C+ 语法法中,类的成成员函数可以以在声明的同同时被定义,并并且自动成为为内联函数。这这虽然会带来来书写上的方方便,但却造造成了风格不不一致,弊大大于利。建议议将成员函数数的定义与声声明分开

19、,不不论该函数体体有多么小。【建议1-2-2】不提倡使用用全局变量,尽尽量不要在头头文件中出现现象exteern innt vallue 这类类声明。/ 版权和版版本声明见示示例1-1,此处处省略。#ifndeffGRAPHHICS_HH/ 防止grapphics.h被重复引引用#defineeGRAPHHICS_HH#includde / 引用标准库库的头文件#includde “myyheadeer.h” / 引用非标准准库的头文件件void Fuunctioon1();/ 全局函函数声明class BBox / 类结构声声明;#endif示例1-2 CC+/C头头文件的结构构1.3 定义

20、文文件的结构定义文件有三部部分内容:定义文件开头处处的版权和版版本声明(参参见示例1-1)。对一些头文件的的引用。程序的实现体(包包括数据和代代码)。假设定义文件的的名称为 ggraphiics.cppp,定义文件的结结构参见示例1-3。/ 版权和版版本声明见示示例1-1,此处处省略。#includde “grraphiccs.h”/ 引用头文件件/ 全局函数数的实现体void Fuunctioon1()/ 类成员函函数的实现体体void Boox:Drraw()示例1-3 CC+/C定定义文件的结结构1.4 头文件件的作用早期的编程语言言如Basic、FFortraan没有头文文件的概念,C

21、C+/C语语言的初学者者虽然会用使使用头文件,但但常常不明其其理。这里对对头文件的作作用略作解释释:(1)通过头文文件来调用库库功能。在很很多场合,源源代码不便(或或不准)向用用户公布,只只要向用户提提供头文件和和二进制的库库即可。用户户只需要按照照头文件中的的接口声明来来调用库功能能,而不必关关心接口怎么么实现的。编编译器会从库库中提取相应应的代码。(2)头文件能能加强类型安安全检查。如如果某个接口口被实现或被被使用时,其其方式与头文文件中的声明明不一致,编编译器就会指指出错误,这这一简单的规规则能大大减减轻程序员调调试、改错的的负担。1.5 目录结结构如果一个软件的的头文件数目目比较多(如

22、如超过十个),通通常应将头文文件和定义文文件分别保存存于不同的目目录,以便于于维护。例如可将头文件件保存于inncludee目录,将定定义文件保存存于sourrce目录(可可以是多级目目录)。如果某些头文件件是私有的,它它不会被用户户的程序直接接引用,则没没有必要公开开其“声明”。为了加强强信息隐藏,这这些私有的头头文件可以和和定义文件存存放于同一个个目录。第2章 程序的的版式版式虽然不会会影响程序的的功能,但会会影响可读性性。程序的版版式追求清晰晰、美观,是是程序风格的的重要构成因因素。可以把程序的版版式比喻为“书法”。好的“书法”可让人对程程序一目了然然,看得兴致致勃勃。差的的程序“书法”

23、如螃蟹爬行行,让人看得得索然无味,更更令维护者烦烦恼有加。请请程序员们学学习程序的“书法”,弥补大学计计算机教育的的漏洞,实在在很有必要。2.1 空行空行起着分隔程程序段落的作作用。空行得得体(不过多多也不过少)将将使程序的布布局更加清晰晰。空行不会会浪费内存,虽虽然打印含有有空行的程序序是会多消耗耗一些纸张,但但是值得。所所以不要舍不不得用空行。【规则2-1-1】在每个类声声明之后、每每个函数定义义结束之后都都要加空行。参参见示例2-1(a)【规则2-1-2】在一个函数数体内,逻揖揖上密切相关关的语句之间间不加空行,其其它地方应加加空行分隔。参参见示例2-1(b )/ 空行void Fuun

24、ctioon1() / 空行void Fuunctioon2() / 空行void Fuunctioon3() / 空行while (condiition)statemment1;/ 空行if (coonditiion) stateement22;elsestateement33;/ 空行statemment4; 示例2-1(aa) 函数之之间的空行 示例例2-1(bb) 函数内内部的空行2.2 代码行行【规则2-2-1】一行代码只只做一件事情情,如只定义义一个变量,或或只写一条语语句。这样的的代码容易阅阅读,并且方方便于写注释释。【规则2-2-2】if、for、whilee、do等语句自自占

25、一行,执执行语句不得得紧跟其后。不不论执行语句句有多少都要要加。这样可可以防止书写写失误。示例2-2(aa)为风格良良好的代码行行,示例2-2(b)为风格不良良的代码行。int widdth;/ 宽度int heiight;/ 高度int deppth;/ 深度int widdth, hheightt, deppth; / 宽度高高度深度x = a + b;y = c + d;z = e + f;X a + b; y = c + dd; z = e + f;if (widdth heighht) dosometthing();if (widdth =”、“=”、“+”、“*”、“%”、“&”、

26、“|”、“”这类操作符符前后不加空空格。【建议2-3-1】对于表达达式比较长的的for语句和和if语句,为为了紧凑起见见可以适当地地去掉一些空空格,如foor (i=0; i10; ii+)和if (a=b) & (c= 2000) / 良好的的风格if(yearr=20000) / 不良良的风格if (a=b) & (c=bb&c=d) / 不良良的风格for (i=0; i10; ii+) / 良好好的风格for(i=00;i100;i+) / 不良的的风格for (i = 0; I 110; i +) / 过多多的空格x = a b ? a : bb; / 良好好的风格x=aFuncct

27、ion(); / 不要写写成 b - Funnctionn();示例2-3 代代码行内的空空格2.4 对齐【规则2-4-1】程序的分界界符和应独占一行行并且位于同同一列,同时时与引用它们们的语句左对对齐。【规则2-4-2】 之内的的代码块在右边数格处处左对齐。示例2-4(aa)为风格良良好的对齐,示示例2-4(b)为风格不不良的对齐。void Fuunctioon(intt x) / prrogramm codeevoid Fuunctioon(intt x) / prrogramm codeeif (connditioon) / prrogramm codeeelse / prrogramm

28、 codeeif (connditioon) / prrogramm codeeelse / prrogramm codeefor (innitiallizatiion; ccondittion; updatte) / prrogramm codeefor (innitiallizatiion; ccondittion; updatte) / prrogramm codeeWhile (condiition) / prrogramm codeewhile (condiition) / prrogramm codee如果出现嵌套的的,则使使用缩进对齐齐,如: 示例2-4(aa) 风格良良好的对齐

29、 示例2-4(b) 风格格不良的对齐齐2.5 长行拆拆分【规则2-5-1】代码行最大大长度宜控制制在70至880个字符以以内。代码行行不要过长,否否则眼睛看不不过来,也不不便于打印。【规则2-5-2】长表达式要要在低优先级级操作符处拆拆分成新行,操操作符放在新新行之首(以以便突出操作作符)。拆分分出的新行要要进行适当的的缩进,使排排版整齐,语语句可读。if (veery_loonger_variaable1 = veery_loonger_variaable122)& (verry_lonnger_vvariabble3 = verry_lonnger_vvariabble14)& (verr

30、y_lonnger_vvariabble5 Draw();/ 类的成成员函数【规则3-1-8】用正确的反反义词组命名名具有互斥意意义的变量或或相反动作的的函数等。例如:intminnValuee;intmaxxValuee;intSettValuee();intGettValuee();【建议3-1-1】尽量避免名名字中出现数数字编号,如如Valuee1,Vallue2等,除除非逻辑上的的确需要编号号。这是为了了防止程序员员偷懒,不肯肯为命名动脑脑筋而导致产产生无意义的的名字(因为为用数字编号号最省事)。3.2 简单的的Windoows应用程程序命名规则则作者对“匈牙牙利”命名规则做做了合理的

31、简简化,下述的的命名规则简简单易用,比比较适合于WWindowws应用软件件的开发。【规则3-2-1】类名和函数数名用大写字字母开头的单单词组合而成成。例如:class Node;/ 类名class LeafNNode;/ 类名void Draw(void);/ 函数名名void SetVaalue(iint vaalue);/ 函数名名【规则3-2-2】变量和参参数用小写字字母开头的单单词组合而成成。例如:BOOL fflag;int ddrawMoode;【规则3-2-3】常量全用用大写的字母母,用下划线线分割单词。例如:const int MMAX = 100;const int MMA

32、X_LEENGTH = 1000;【规则3-2-4】静态变量加前前缀s_(表示staatic)。例如:void Innit()staticc int s_iniitValuue;/ 静态变变量【规则3-2-5】如果不得得已需要全局局变量,则使使全局变量加加前缀g_(表示gloobal)。例如:int g_hhowMannyPeopple;/ 全局变变量int g_hhowMucchMoneey;/ 全局变变量【规则3-2-6】类的数据据成员加前缀缀m_(表示示membeer),这样样可以避免数数据成员与成成员函数的参参数同名。例如:void OObjectt:SettValuee(int wi

33、dthh, intt heigght)m_widdth = widthh;m_heighht = hheightt;【规则3-2-7】为了防止止某一软件库库中的一些标标识符和其它它软件库中的的冲突,可以以为各种标识识符加上能反反映软件性质质的前缀。例例如三维图形形标准OpeenGL的所所有库函数均均以gl开头头,所有常量量(或宏定义义)均以GLL开头。3.3 简单的的Unix应用用程序命名规规则第4章 表达式式和基本语句句读者可能怀疑:连if、for、whilee、goto、switcch这样简单单的东西也要要探讨编程风风格,是不是是小题大做?我真的发觉很多多程序员用隐隐含错误的方方式写表达式

34、式和基本语句句,我自己也也犯过类似的的错误。表达式和语句都都属于C+/C的短语语结构语法。它它们看似简单单,但使用时时隐患比较多多。本章归纳纳了正确使用用表达式和语语句的一些规规则与建议。4.1 运算符符的优先级C+/C语语言的运算符符有数十个,运运算符的优先先级与结合律律如表4-11所示。注意意一元运算符符 + - * 的的优先级高于于对应的二元元运算符。优先级运算符结合律从高到低排列( ) - .从左至右! + - (类型型) sizzeof+ - * &从右至左* / %从左至右+ -从左至右从左至右 =从左至右= !=从左至右&从左至右从左至右|从左至右&从左至右|从右至左?:从右至左

35、= += -= *= /= %= &= =|= =从左至右表4-1 运算算符的优先级级与结合律【规则4-1-1】如果代代码行中的运运算符比较多多,用括号确确定表达式的的操作顺序,避避免使用默认认的优先级。由于将表4-11熟记是比较较困难的,为为了防止产生生歧义并提高高可读性,应应当用括号确确定表达式的的操作顺序。例例如:word = (highh = bb & cc d & c + f = g + h ;/ 复合表表达式过于复复杂【规则4-2-2】不要有多多用途的复合合表达式。例如:d = (a = b + c) + r ; 该表达式既求aa值又求d值值。应该拆分分为两个独立立的语句:a =

36、b + c;d = a + r;【规则4-2-3】不要把程程序中的复合合表达式与“真正的数学学表达式”混淆。例如:if (a b c)/ a b c是数学表达式式而不是程序序表达式并不表示if (ab) & (bcc)而是成了令人费费解的if ( (aab)=”或“=-EPSINNON) & (x=EPSIINON)其中EPSINNON是允许许的误差(即即精度)。4.3.4 指指针变量与零零值比较【规则4-3-4】应当将指针变变量用“=”或“!=”与NULL比较较。指针变量的零零值是“空”(记为NULLL)。尽管管NULL的值值与0相同,但但是两者意义义不同。假设设指针变量的的名字为p,它与零

37、值值比较的标准准if语句如下下:if (pp = NNULL)/ p与NULL显式式比较,强调调p是指针变量量if (pp != NNULL)不要写成if (pp = 00) / 容易让让人误解p是整型变量量if (pp != 00) 或者if (p)/ 容易让让人误解p是布尔变量量if (!pp)4.3.5 对对if语句的补补充说明有时候我们可能能会看到 iif (NUULL = p) 这这样古怪的格格式。不是程程序写错了,是是程序员为了了防止将 iif (p = NUULL) 误误写成 iff (p = NULLL),而有意意把p和NULL颠倒倒。编译器认认为 if (p = NULL)

38、是合法的的,但是会指指出 if (NULLL = p)是错误的,因因为NULLL不能被赋值值。程序中有时会遇遇到if/eelse/rreturnn的组合,应应该将如下不不良风格的程程序if (coonditiion)returrn x;returnn y;改写为if (coonditiion)returrn x;elsereturn y;或者改写成更加加简练的 return (condditionn ? x : y);4.4 循环语语句的效率C+/C循循环语句中,for语句使用频率最高,while语句其次,do语句很少用。本节重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。

39、【建议4-4-1】在多重循环环中,如果有有可能,应当当将最长的循循环放在最内内层,最短的的循环放在最最外层,以减减少CPU跨切循循环层的次数数。例如示例例4-4(b)的效率率比示例4-4(a)的高。for (roow=0; row1100; rrow+)for ( ccol=0; col5; cool+ )sum = ssum + arowwcoll;for (cool=0; col55; coll+ )for (roow=0; row1100; rrow+) summ = suum + aarowcol;示例4-4(aa) 低效率率:长循环在在最外层 示示例4-4(b) 高效效率:长循环环在

40、最内层【建议4-4-2】如果循环体体内存在逻辑辑判断,并且且循环次数很很大,宜将逻逻辑判断移到到循环体的外外面。示例44-4(c)的程序序比示例4-4(d)多执行行了N-1次逻辑辑判断。并且且由于前者老老要进行逻辑辑判断,打断断了循环“流水线”作业,使得得编译器不能能对循环进行行优化处理,降降低了效率。如果N非常大,最好采用示例4-4(d)的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例4-4(c)的写法比较好,因为程序更加简洁。for (i=0; iN; i+)if (connditioon) DoSSomethhing();else DoOOthertthing();if

41、 (connditioon)for (i=0; iN; i+) DoSSomethhing();else forr (i=00; iNN; i+) DoOOthertthing();表4-4(c) 效率低但但程序简洁 表4-4(d) 效率率高但程序不不简洁4.5 forr 语句的循循环控制变量量【规则4-5-1】不可在ffor 循环环体内修改循循环变量,防防止for 循环失去控控制。【建议4-5-1】建议for语句的的循环控制变变量的取值采采用“半开半闭区区间”写法。示例4-5(aa)中的x值属于半开开半闭区间“0 = x N”,起点到终终点的间隔为为N,循环次数数为N。示例4-5(bb)中的

42、x值属于闭区区间“0 = x = N-1”,起点到终终点的间隔为为N-1,循环环次数为N。相比之下,示例例4-5(a)的写法法更加直观,尽尽管两者的功功能是相同的的。for (innt x=00; xNN; x+)for (innt x=00; x 0 )*pbTTo + = *pbbFrom + ;returrn pvTTo;示例6-5 复复制不重叠的的内存块assert不不是一个仓促促拼凑起来的的宏。为了不不在程序的DDebug版版本和Rellease版版本引起差别别,assertt不应该产生生任何副作用用。所以asssert不不是函数,而而是宏。程序序员可以把aassertt看成一个在在

43、任何系统状状态下都可以以安全使用的的无害测试手手段。如果程程序在asssert处终终止了,并不不是说含有该该asserrt的函数有有错误,而是是调用者出了了差错,asssert可可以帮助我们们找到发生错错误的原因。很少有比跟踪到到程序的断言言,却不知道道该断言的作作用更让人沮沮丧的事了。你你化了很多时时间,不是为为了排除错误误,而只是为为了弄清楚这这个错误到底底是什么。有有的时候,程程序员偶尔还还会设计出有有错误的断言言。所以如果果搞不清楚断断言检查的是是什么,就很很难判断错误误是出现在程程序中,还是是出现在断言言中。幸运的的是这个问题题很好解决,只只要加上清晰晰的注释即可可。这本是显显而易见

44、的事事情,可是很很少有程序员员这样做。这这好比一个人人在森林里,看看到树上钉着着一块“危险”的大牌子。但但危险到底是是什么?树要要倒?有废井井?有野兽?除非告诉人人们“危险”是什么,否否则这个警告告牌难以起到到积极有效的的作用。难以以理解的断言言常常被程序序员忽略,甚甚至被删除。Maguire, p8-p30【规则6-5-1】使用断言捕捕捉不应该发发生的非法情情况。不要混混淆非法情况况与错误情况况之间的区别别,后者是必必然存在的并并且是一定要要作出处理的的。【规则6-5-2】在函数的的入口处,使使用断言检查查参数的有效效性(合法性性)。【建议6-5-1】在编写函函数时,要进进行反复的考考查,并

45、且自自问:“我打算做哪哪些假定?”一旦确定了了的假定,就就要使用断言言对假定进行行检查。【建议6-5-2】一般教科科书都鼓励程程序员们进行行防错设计,但但要记住这种种编程风格可可能会隐瞒错错误。当进行行防错设计时时,如果“不可能发生生”的事情的确确发生了,则则要使用断言言进行报警。6.6 引用与与指针的比较较引用是C+中中的概念,初初学者容易把把引用和指针针混淆一起。一一下程序中,n是m的一个引用(reference),m是被引用物(referent)。int m;int &nn = m;n相当于m的别别名(绰号),对对n的任何操作作就是对m的操作。例例如有人名叫叫王小毛,他他的绰号是“三毛”

46、。说“三毛”怎么怎么的的,其实就是是对王小毛说说三道四。所所以n既不是m的拷贝,也也不是指向mm的指针,其其实n就是m它自己。引用的一些规则则如下:(1)引用被创创建的同时必必须被初始化化(指针则可可以在任何时时候被初始化化)。(2)不能有NNULL引用用,引用必须须与合法的存存储单元关联联(指针则可可以是NULLL)。(3)一旦引用用被初始化,就就不能改变引引用的关系(指指针则可以随随时改变所指指的对象)。以下示例程序序中,k被初始化为为i的引用。语语句k = j并不能将k修改成为j的引用,只只是把k的值改变成成为6。由于于k是i的引用,所所以i的值也变成成了6。int i = 5;int

47、j = 6;int &kk = i;k = j;/ k和i的值都变成成了6;上面的程序看看起来象在玩玩文字游戏,没没有体现出引引用的价值。引引用的主要功功能是传递函函数的参数和和返回值。CC+语言中中,函数的参参数和返回值值的传递方式式有三种:值值传递、指针针传递和引用用传递。以下是“值传传递”的示例程序序。由于Fuunc1函数数体内的x是外部变量n的一份拷贝贝,改变x的值不会影影响n, 所以n的值仍然是是0。void FFunc1(int xx)x = x + 10;int n = 0;Func1(n);cout “n = ” n enddl;/ n = 0以下是“指针传传递”的示例程序序。

48、由于Fuunc2函数数体内的x是指向外部部变量n的指针,改改变该指针的的内容将导致致n的值改变,所所以n的值成为110。void FFunc2(int *x)(* x) = (* x) + 10;int n = 0;Func2(&n);cout “n = ” n enddl;/ n = 10以下是“引用用传递”的示例程序序。由于Fuunc3函数数体内的x是外部变量量n的引用,x和n是同一个东东西,改变xx等于改变n,所以n的值成为110。void FFunc3(int &x)x = x + 10;int n = 0;Func3(n);cout “n = ” n enddl;/ n = 10对比

49、上述三个个示例程序,会会发现“引用传递”的性质象“指针传递”,而书写方方式象“值传递”。实际上“引用”可以做的任任何事情“指针”也都能够做做,为什么还还要“引用”这东西?答案是“用适当当的工具做恰恰如其分的工工作”。指针能够毫无无约束地操作作内存中的如如何东西,尽尽管指针功能能强大,但是是非常危险。就就象一把刀,它它可以用来砍砍树、裁纸、修修指甲、理发发等等,谁敢敢这样用?如果的确只需要要借用一下某某个对象的“别名”,那么就用用“引用”,而不要用用“指针”,以免发生生意外。比如如说,某人需需要一份证明明,本来在文文件上盖上公公章的印子就就行了,如果果把取公章的的钥匙交给他他,那么他就就获得了不

50、该该有的权利。第7章 内存管管理欢迎进入内存存这片雷区。伟伟大的Billl Gattes 曾经经失言:640K ouught tto be enouggh forr everrybodyy Bill Gatess 19811程序员们经常编编写内存管理理程序,往往往提心吊胆。如如果不想触雷雷,唯一的解解决办法就是是发现所有潜潜伏的地雷并并且排除它们们,躲是躲不不了的。本章的内容容比一般教科科书的要深入入得多,读者者需细心阅读读,做到真正正地通晓内存存管理。7.1内存分配配方式内存分配方式有有三种:从静态存储区域域分配。内存存在程序编译译的时候就已已经分配好,这这块内存在程程序的整个运运行期间都存

51、存在。例如全全局变量,sstaticc变量。在栈上创建。在在执行函数时时,函数内局局部变量的存存储单元都可可以在栈上创创建,函数执执行结束时这这些存储单元元自动被释放放。栈内存分分配运算内置置于处理器的的指令集中,效效率很高,但但是分配的内内存容量有限限。从堆上分配,亦亦称动态内存存分配。程序序在运行的时时候用mallloc或new申请任任意多少的内内存,程序员员自己负责在在何时用frree或delette释放内存存。动态内存存的生存期由由我们决定,使使用非常灵活活,但问题也也最多。7.2常见的内内存错误及其其对策发生内存错误误是件非常麻麻烦的事情。编编译器不能自自动发现这些些错误,通常常是在

52、程序运运行时才能捕捕捉到。而这这些错误大多多没有明显的的症状,时隐隐时现,增加加了改错的难难度。有时用用户怒气冲冲冲地把你找来来,程序却没没有发生任何何问题,你一一走,错误又又发作了。常见的内存错误误及其对策如如下:内存分配未成功功,却使用了了它。编程新手常犯这这种错误,因因为他们没有有意识到内存存分配会不成成功。常用解解决办法是,在在使用内存之之前检查指针针是否为NUULL。如果果指针p是函数的参参数,那么在在函数的入口口处用asssert(pp!=NULLL)进行检检查。如果是是用mallloc或new来申请请内存,应该该用if(pp=NULLL) 或if(pp!=NULLL)进行防防错处

53、理。内存分配虽然成成功,但是尚尚未初始化就就引用它。犯这种错误主要要有两个起因因:一是没有有初始化的观观念;二是误误以为内存的的缺省初值全全为零,导致致引用初值错错误(例如数数组)。内存的缺省初值值究竟是什么么并没有统一一的标准,尽尽管有些时候候为零值,我我们宁可信其其无不可信其其有。所以无无论用何种方方式创建数组组,都别忘了了赋初值,即即便是赋零值值也不可省略略,不要嫌麻麻烦。内存分配成功并并且已经初始始化,但操作作越过了内存存的边界。例如在使用数组组时经常发生生下标“多1”或者“少1”的操作。特特别是在foor循环语句句中,循环次次数很容易搞搞错,导致数数组操作越界界。忘记了释放内存存,造

54、成内存存泄露。含有这种错误的的函数每被调调用一次就丢丢失一块内存存。刚开始时时系统的内存存充足,你看看不到错误。终终有一次程序序突然死掉,系系统出现提示示:内存耗尽尽。动态内存的申请请与释放必须须配对,程序序中mallloc与free的使使用次数一定定要相同,否否则肯定有错错误(neww/deleete同理)。释放了内存却继继续使用它。有三种情况:(1)程序中的的对象调用关关系过于复杂杂,实在难以以搞清楚某个个对象究竟是是否已经释放放了内存,此此时应该重新新设计数据结结构,从根本本上解决对象象管理的混乱乱局面。(2)函数的rreturnn语句写错了了,注意不要要返回指向“栈内存”的“指针”或者

55、“引用”,因为该内内存在函数体体结束时被自自动销毁。(3)使用frree或delette释放了内内存后,没有有将指针设置置为NULLL。导致产生生“野指针”。【规则7-2-1】用mallooc或new申请内内存之后,应应该立即检查查指针值是否否为NULLL。防止使用用指针值为NNULL的内内存。【规则7-2-2】不要忘记记为数组和动动态内存赋初初值。防止将未被初初始化的内存存作为右值使使用。【规则7-2-3】避免数组组或指针的下下标越界,特特别要当心发发生“多1”或者“少1”操作。【规则7-2-4】动态内存存的申请与释释放必须配对对,防止内存存泄漏。【规则7-2-5】用free或delette

56、释放了内内存之后,立立即将指针设设置为NULLL,防止产产生“野指针”。7.3指针与数数组的对比C+/C程程序中,指针针和数组在不不少地方可以以相互替换着着用,让人产产生一种错觉觉,以为两者者是等价的。数组要么在静静态存储区被被创建(如全全局数组),要要么在栈上被被创建。数组组名对应着(而而不是指向)一一块内存,其其地址与容量量在生命期内内保持不变,只只有数组的内内容可以改变变。指针可以随时指指向任意类型型的内存块,它它的特征是“可变”,所以我们们常用指针来来操作动态内内存。指针远远比数组灵活活,但也更危危险。下面以字符串为为例比较指针针与数组的特特性。7.3.1 修修改内容示例7-3-1中,

57、字符数数组a的容量是66个字符,其其内容为heello00。a的内容可以以改变,如aa0= X。指指针p指向常量字字符串“worldd”(位于静态存储区区,内容为wworld0),常量量字符串的内内容是不可以以被修改的。从从语法上看,编编译器并不觉觉得语句p0= X有什么么不妥,但是是该语句企图图修改常量字字符串的内容容而导致运行行错误。char a = “hhello”;a0 = X;cout a endll;char *pp = “wworld”; / 注意意p指向常量字字符串p0 = X; / 编编译器不能发发现该错误cout p endll;示例7-3-11 修改数组和和指针的内容容7

58、.3.2 内内容复制与比比较不能对数组名名进行直接复复制与比较。示示例7-3-2中,若想想把数组a的内容复制制给数组b,不能用语语句 b = a ,否否则将产生编编译错误。应应该用标准库库函数strrcpy进行行复制。同理理,比较b和a的内容是否否相同,不能能用if(bb=a) 来判断,应该该用标准库函函数strccmp进行比比较。语句p = a 并不能能把a的内容复制制指针p,而是把a的地址赋给给了p。要想复制制a的内容,可可以先用库函函数mallloc为p申请一块容容量为strrlen(aa)+1个字字符的内存,再再用strccpy进行字字符串复制。同同理,语句if(p=a) 比比较的不是

59、内内容而是地址址,应该用库库函数strrcmp来比比较。/ 数组char aa = helllo;char bb10;strcpyy(b, aa);/ 不能能用b = aa;if(strrcmp(bb, a) = 0)/ 不能能用 iff (b = a)/ 指针int leen = sstrlenn(a);char *p = (char *)mallloc(ssizeoff(charr)*(leen+1);strcpyy(p,a);/ 不要用用 p = a;if(strrcmp(pp, a) = 0)/ 不要要用 if (p = a)示例7-3-22 数组和指针针的内容复制制与比较7.3.3

60、计计算内存容量量用运算符siizeof可可以计算出数数组的容量(字字节数)。示示例7-3-3(a)中,sizzeof(aa)的值是112(注意别别忘了00)。指针针p指向a,但是sizzeof(pp)的值却是是4。这是因因为sizeeof(p)得到的是一一个指针变量量的字节数,相相当于sizzeof(cchar*),而不是p所指的内存存容量。C+/C语言言没有办法知知道指针所指指的内存容量量,除非在申申请内存时记记住它。注意当数组作为为函数的参数数进行传递时时,该数组自自动退化为同同类型的指针针。示例7-3-3(b)中,不论论数组a的容量是多多少,sizzeof(aa)始终等于于sizeoof

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论