编写高质量代码:改善C程序代码的125个建议.html.pdf
《编写高质量代码:改善C程序代码的125个建议.html.pdf》由会员分享,可在线阅读,更多相关《编写高质量代码:改善C程序代码的125个建议.html.pdf(243页珍藏版)》请在三一文库上搜索。
1、前言 为什么写作本书 众所周知,C语言是一门既具有高级语言特点,又有汇编语言特点的通用计算机编程语言,无论是操作系统(如Microsoft Windows、Mac OS X、Linux和UNIX等)、嵌入式系统与普通应用软件,还 是目前流行的移动智能设备开发,随处都可以看见它依然矫健的身影。它能够轻松地应用于各类层次的开发中,从设备驱动程序和操作系统组件到大规模应用程序,它都能够很好地胜任。毋庸置疑,它是 二十几年来使用最为广泛、生命力最强的编程语言,它的设计思想也影响了众多后来的编程语言,例如C+、Objective-C、Java、C#等。 尽管C语言有着悠久的历史和广泛的使用场景,但它依旧
2、让大部分计算机编程人员望而生畏,相信绝大多数读者也还停留在“入门者”这个阶段。所谓“入门者”指的是已经可以简单使用C语言编写普 通应用程序,但是却不明白如何编写高质量代码的人。面对这样的实际情况,在准备编写本书之前,一连串的问题深深地映入笔者的脑海:到底什么样的编程书籍才能够帮助“入门者”快速进阶?面对市 面上众多的优秀C语言编程书籍,编写本书的价值何在?怎样的内容才能够与众不同? 带着这一连串的问题,笔者开始回顾自己这些年的开发生涯,发现如下几类问题经常困扰“入门者”: 基础数据类型问题:如数据取值范围、整数溢出与回绕、浮点数精度、数据类型转换的范围检查等。 数组与指针问题:指针与地址、野指
3、针、空(null)指针、NULL指针、void指针、多级指针、指针函数与函数指针,以及数组越界与缓冲区溢出等。 内存管理问题:内存分配、内存释放、内存越界与内存泄漏等。 字符与字符串问题:串拷贝与内存拷贝,内存重叠与溢出,字符串查找等。 高效设计问题:表达式设计、算法设计与函数设计,内联函数与宏的取舍等。 其他杂项问题:信号处理、文件系统、断言与异常处理、内嵌汇编的使用等。 如果你同样也苦于处理这些问题,或者对这些问题模棱两可,那么本书正是为你所准备的。本书为普遍存在于初级与中级开发者脑海中的那些问题给出了经验性的解决方案。全书分为15章,通过125 个建议深度剖析C语言程序设计中的常见性问题
4、,并给出经验性的解决方案。除此之外,为了使读者能够尽量做到“知其所以然”,本书重点阐述了一些尖锐的问题,如IEEE 754浮点数、指针与数组、越 界与溢出等问题。当然,这些经验和心得的积累并非我一人之力,“我只不过是站在巨人的肩膀上而已”。因此,在撰写本书的过程中也参考了大量的资料,如www.securecoding.cert.org的SEI CERT C Coding Standard、ISO/IEC 9899:1990、ISO/IEC 9899:1999与ISO/IEC 9899:201x标准文档等。 如何阅读本书 本书适合那些有一定C语言基础并希望快速提升程序设计能力的初级与中级程序员。
5、因此,本书并不会阐述C语言中的一些基础概念,而是将C语言编程过程中可能遇到的疑问或者障碍进行一一列举与 剖析,并给出了经验性解决方案与建议。 如果你是一位有一定C语言编程基础的初中级读者,本书就是为你量身打造的。你可以逐章进行系统性学习,并结合我们提供的源码动手实践,巩固所学的知识。书中的大多数建议实战性很强,要完 全理解其中的奥妙,请果断地放弃printf函数,多调试一下程序,编程高手都是调试出来的;如果你是一位编程经验非常丰富的高级读者,那么可以将书中的大部分经验与自己的一些经验进行融合,从而获 得更多提高与升华。 资源及勘误 通常情况下,一个问题的解决方案往往不止一种,你可能会不同意本书
6、中的一些观点,甚至强烈反对。同时,尽管笔者在本书的写作过程中非常认真与努力,但由于水平有限,书中难免存在错误和不 足之处,恳请批评指正。如果你对本书有什么意见、问题或想法,欢迎使用下面的邮箱通知笔者,笔者将不胜感激。当然,也可以通过微信(sc-mawei)与笔者取得联系,共同进行技术交流。 Email: 特别鸣谢 最后,要感谢那些所有帮助过笔者的人,没有他们的帮助与付出,这本书很难顺利完成。尤其要感谢下面这些人: 首先,机械工业出版社的杨福川与姜影为本书的整体策划、审阅和出版做了大量的工作,与他们的合作是非常愉快的。同时,由于写作过程漫长,难免令笔者情绪波动,是他们给了我一如既往的支持 与鼓励
7、,当我想要放弃的时候,是他们的敦促让我对写作时刻保持着热情,坚持完成本书。也正因为他们对本书的不断要求,才使得本书的结构更加系统化,内容更加深刻,语言更加简单易懂。 其次,要感谢家人的支持。为了编写本书,笔者投入了大量的时间和精力,牺牲了许多可以陪家人的周末和节假日。 最后,要感谢那些曾经为本书的编写提过意见的朋友,感谢他们对本书的默默支持。 马伟 第1章 数据,程序设计之根本 数据是程序设计最基础的概念,程序对数据进行操作。换句话说,任何一个完整的程序都可以看成是一组数据和作用于这组数据上的操作的说明。同时,程序中的每个数据项也都有一个与之相关的类 型,称为“数据类型”。 这样,在程序中就可
8、以使用数据类型来区分不同的数据,进而根据实际需要为这些数据分配不同的存储空间。这就像成年人必须睡成人床,而给婴儿配备婴儿床就足够了,如果你给婴儿分配一张成人 床就会造成资源浪费,相反给成年人分配一张婴儿床则有可能会发生“溢出”。数据类型也一样,由于不同的数据所需要的存储容量各不相同,因此需要分配的内存空间大小也会不一样,这样才能够保证 内存资源的合理配置,使程序性能达到最优化。因此,如何合理、安全地使用这些数据类型是每个程序员必须掌握的。本章将围绕这一话题进行讨论。 建议1:认识ANSI C 谈到C语言的发展历程,就不得不从最早的二进制语言说起。大家都知道,二进制语言可以说是世界上最早的计算机
9、语言,它只允许程序设计人员使用计算机能够直接识别和执行的二进制代码(即0和 1,其中,0代表低电压,1代表高电压)来编写程序。可想而知,这样的编码方式对程序设计人员来说是多么困难与枯燥。因此,为了提高程序设计效率并减轻程序设计人员的负担,所以后来很快便出现 了汇编语言(Assembly Language)。 与二进制语言一样,汇编语言也是面向机器的程序设计语言,不同类型的计算机上需要提供不同的汇编语言。但与二进制语言不同的是,汇编语言使用助记符(Memoni)来代替操作码,并用地址符 号(Symbol)或标号(Label)来代替地址码。由于汇编语言采用符号代替二进制代码,因此,它也被称为符号语
10、言。使用汇编语言编写的程序机器并不能直接识别,而需要通过一种程序将汇编语言翻译 成机器能够识别的二进制语言,这种起翻译作用的程序就是汇编程序。 相对于二进制语言,汇编语言不仅使开发效率得到了很大提升,而且它还具有许多优点,比如它能够直接同计算机的底层软件或硬件进行交互,直接访问与硬件相关的存储器或I/O端口;能够不受编译 器的限制,对生成的二进制代码进行完全控制;能够对关键的代码进行更准确地控制,避免因线程共同访问或硬件设备共享而引起的死锁;能够根据特定的应用对代码进行最佳优化,提高运行速度等。 尽管如此,汇编语言依旧是一种层次非常低的语言,它仅仅高于直接手工编写二进制的机器指令码。在实际应用
11、中,它仍然暴露了一些不可避免的缺陷:如编写的代码非常难以阅读,不好维护;很容 易产生bug,难于调试;一般只能针对特定的体系结构和处理器进行优化;开发效率很低,时间长且单调等。因此,我们更加需要一种设计描述简单,能脱离对机型的要求,并且能在任何计算机上运行的 计算机语言,我们称这种语言为高级语言。这样,程序设计人员就可以将问题及解决问题的算法过程描述出来,利用这种高级语言直接写出各种表达式来描述简单的计算过程,而无须针对不同的机型编写 不同的代码。 注释 高级语言编写的程序称为源程序,源程序不能在计算机上直接运行,必须将其翻译成二进制代码后才能执行。一般有两种翻译方式:一种是“解释程序”方式,
12、即将源程序作为输入,翻译一 句后就提交计算机执行一句,这种方式并不形成目标程序;另一种是“编译程序”方式,即将源程序作为输入,全部翻译成二进制代码后再执行,编译后的二进制程序称为目标程序。 世界上出现的第一种高级语言是Algol语言,它也可以算作C语言的前身。它和普通语言表达式非常接近,适用于数值计算,所以Algol多用于科学计算机。1960年Algol 60版本推出后,很受程序设计 人员欢迎。Algol 60推出了许多新的概念,如局部性概念、动态、递归、巴科斯-诺尔范式(Backus-Naur Form,BNF)等。从某种意义上讲,Algol 60应该是程序设计语言发展史上的一个里程碑,它标
13、 志着程序设计语言已成为一门独立的科学学科,并为后来的软件自动化及软件可靠性的发展奠定了基础。 虽然使用Algol 60来描述算法很方便,但是它离计算机硬件系统却很远,不宜用来编写系统程序。1963年英国剑桥大学在Algol语言的基础上增添了处理硬件的能力,并命名为“CPL”(Combined Programming Language),即复合程序设计语言。但由于CPL的规模很大,学习和掌握都比较困难,因此没有流行。1967年剑桥大学的Martin Richards对CPL语言进行了简化,推出BCPL(Basic Combined Programming Language),即基本复合程序设计
14、语言。它是典型的面向过程的高级语言,它的语法更加靠近机器本身,适合于开发精巧、高要求的应用程序,而且它对编译器的要求也不 高。同时,BCPL也是最早使用库函数封装基本输入输出的语言之一,这使得它的跨平台可移植性很好。 1969年,美国通用电气公司、麻省理工学院与贝尔实验室联合创建了一个庞大的项目,命名为Muktics工程。该项目的目的是创建一个操作系统,不过由于该项目过于复杂和庞大,最终失败了。这也 致使项目的参与者之一通用电气公司退出软件领域,同时,贝尔实验室的专家们也撤出了Muktics工程,转而研究新的领域。之后,贝尔实验室的一位名为Ken Thompson研究员和他的同事Dennis
15、Ritchie组成一个非正式的小组,开始进行一些其他方面的研究。为了自娱自乐,Ken Thompson把他的“太空旅行”软件移植到不太常用的PDP-7系统上。与此同时,Ken Thompson还为PDP-7系统编写 了一个简单的操作系统。该操作系统比起Muktics工程简单了许多,采用汇编语言编写,1970年Brian Kernighan为其取名为UNIX。 注意 从这里可以看出,著名的操作系统UNIX是早于C语言出现的,后来才用C语言重写此系统,这一点一定要注意。 不过使用汇编语言编写程序不仅吃力而且效率低下,所以Ken Thompson就考虑利用高级语言的特性来解决这一问题。1970年,K
16、en Thompson进一步简化了BCPL,突出硬件的处理能力,并 取“BCPL”的第一个字母“B”作为新语言的名称,即B语言。同时,他还使用B语言编写了UNIX操作系统程序。不过B语言还是存在许多问题,最大问题就在于无法表达不同的数据类型,而且效率不高, 这也迫使Ken Thompson后来不得不在PDP-11的基础上重新使用汇编语言来实现UNIX。面对B语言存在的问题,1971年Dennis Ritchie通过增加类型扩展了B语言,这次采用的是编译模式而不是解释模 式,并且引入了类型系统,每个变量在使用前必须声明。这种扩展的B语言称为NB,即New B的缩写。 1972年,Ken Thom
17、pson和Dennis Ritchie继续对B语言进行完善和扩充,他们在保留B语言强大硬件处理能力的基础上,扩充了数据类型,恢复了通用性,并取“BCPL”的第二个字母“C”作为新语 言的名称,即C语言。其实,C语言除了增加类型系统外,它还增加了许多方便编译器设计者设计的新特性,主要表现在以下几个方面: 数组下标从0开始,而不是从1开始。例如,我们定义一个数组arr50,因为C语言的数组下标是从0开始的,所以它的合法范围是arr0arr49。因此,你不能够向arr50里存储数据。 可以把数组看作指针,它简化了参数的传递方法,使大家不必忍受传递一个数组到函数时需要复制所有数组内容的低效率。不过,值
18、得注意的是,数组与指针并非在任何情况下都是等效的,这一点 会在后面进行详细阐述。 float类型被自动扩展为double类型。虽然在ANSI C中情况不再如此,但最初浮点数常量的精度都是double类型的,所有表达式中float类型的变量总会被自动转化为double类型。 增加register关键字,用此关键字告诉编译器设计者哪些变量被放到了寄存器中,从而简化编译器,但却也因此给程序员带来了无穷无尽的麻烦,这一点会在后面的章节中详细阐述。 此后,两人又合作重写了UNIX操作系统,C语言也伴随着UNIX操作系统成为一种广受欢迎的计算机语言。图1-1按时间顺序阐述C语言的由来。 图1-1 C语言的
19、由来 1978年,为了让C语言脱离UNIX操作系统,成为任何计算机上都能运行的通用计算机语言,Brian Kernighan和Dennis Ritchie共同撰写了The C Programming Language的第1版,该著作简称 为“K&R”。书中对C语言的语法进行了规范化描述,书末的参考指南则给出了当时C语言的完整定义,这也成为当时C语言事实上的标准,此标准称为“K&R C”。从此以后,C语言被移植到各种机型 上,并受到广泛支持。 随着C语言在多个领域的推广和应用,一些新的特性不断被各种编译器实现并添加进来。于是建立一个新的“无歧义、与具体平台无关的C语言定义”就成为越来越重要的事情
20、。1983年,美国国家标准 委员会ANSI(American National Standards Institute)属下专门负责信息技术标准化的机构ASC X3(现已更名为国际信息技术标准委员会(International Committee for Information Technology Standards,INCITS)成立了一个专门的技术委员会J11(J11是委员会编号,全称是X3J11),用于起草关于C语言的标准草案。1989年,ANSI正式通过C语言标准草案,至此该标准成为美国国家标 准,此标准也称为C89标准。 随后,The C Programming Language第
21、2版出版发行,书中内容根据ANSI C(C89)进行了更新。1990年,在ISO/IEC JTC1/SC22/WG14(即ISO/IEC联合技术第I委员会第22分委员会第14工 作组)的努力下,ISO批准ANSI C成为国际标准,于是ISO C(又称为C90)诞生。与C89相比,C90除了标准文档的印刷编排细节有些不同外(主要表现在删除了“Rationale”一节,并把文档的格式与段 落编码作了改动),它们在技术上是完全一样的。到目前为止,C89是C语言运用得最广泛的标准,基本上所有的C语言编译器都完全支持该标准。相对于“K&R C”,C89主要做了以下几方面的改进: 增加了新特性原型。原型是
22、函数声明的扩展,它使得编译器很容易根据函数的定义检查函数的用法。 增加了一些新的关键字,如enum、const、volatile、signed与void。C89的关键字见表1-1。 表1-1 C89关键字表 除此之外,C89还做了许多其他的改进,如增强了预处理指令,定义了相关的宏,允许将结构本身作为参数传递给函数,从“无符号保留”转到“值保留”等。 自ISO C(C90)推出之后,ISO又于1994年与1996年分别出版了C90的技术勘误文档,更正了一些印刷错误,同时,在1995年还通过了一份C90的技术补充,这份补充对C90进行了微小扩充,扩充后 的ISO C被称为C95。 1999年,AN
23、SI和ISO又通过了最新版本的C语言标准和技术勘误文档,该标准被称为C99。这里需要说明的是,与C89不同,并非市面上所有的编译器都支持C99,并且有的编译器只支持C99的部分新 特性。相对于C89,C99主要做了以下几方面的改进: 增加了restrict与inline关键字。 新增_Bool、_Complex与_Imaginary 3种数据类型,如C99中定义的复数类型为:float_Complex、float_Imaginary、double_Complex、double_Imaginary、long double_Complex与long double_Imaginary。 增强数组的功
24、能,支持可变长数组等。 支持复合赋值。 增强预处理程序,如引入_Pragma运算符,并增加了一些内部宏等。 支持柔性数组结构成员,即允许结构中的最后一个元素是未知大小的数组。 由于技术的发展日新月异,因此虽然C99还没有得到完全支持,但在2007年,标准委员会就又开始起草新的C语言标准来取代现有的C99标准,该标准命名为C1X,C1X是一个非正式名字。2011年12 月,ANSI正式采纳了ISO/IEC 9899:2011标准,即C11标准。相对于C99,C11主要做了如下几方面的改进: 采用新的对齐规范,包括_Alignas说明符、_Alignof运算符、aligned_alloc函数与头文
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 编写 质量 代码 改善 程序代码 125 建议 html
链接地址:https://www.31doc.com/p-5514588.html