C语言程序设计:现代方法(第2版)
上QQ阅读APP看书,第一时间看更新

前言

在计算领域中,把显而易见的转变为有实用价值的,这一过程是“挫折”一词的生动体现。

自本书第1版出版以来,基于C的语言大量兴起(其中最杰出的代表是Java和C#),已有的C++和Perl等相关语言也取得了更大的成就。尽管如此,C语言仍然像当年一样流行,仍然悄无声息地掌控着世界上的许多软件。跟1996年一样,C语言仍然是计算机领域里的通用语言。

但即便是C语言也必须随着时间而发展。C99标准的发布催生了对本书新版的需求,而且,第1版涉及的DOS和16位处理器也已经趋于过时。本版对内容进行了全面更新,并在其他许多方面进行了改进。

下面列出了本版的新特色和所做的改进。

·完整地覆盖了C89标准和C99标准。本版和第1版最大的差别就在于覆盖了C99标准。我的目标是覆盖C89和C99之间的每一个重要差别,包括C99新增的所有语言特性和库函数。C99中的每一处改变都会清楚地标出来,或者在小节标题中加上“C99”字样,或者在讨论比较简短时在正文的左边空白处用一个专门的图标表示。我这样做有两个目的:一是提醒读者注意C99中的改变,二是让那些对C99不感兴趣或没有C99编译器的读者知道哪些内容可以跳过。C99新增的许多内容可能只有特定的读者会感兴趣,但有些新特性几乎对所有的C程序员都有用。

·提供了对所有C89和C99库函数的快速参考。第1版中附录D介绍了C89的所有标准库函数,本版的附录D给出了C89和C99的所有库函数。

·扩展了GCC的内容。自本书第1版出版以来,GCC(最初是GNU C Compiler的简称,现在指GNU Compiler Collection)得到了广泛应用。GCC有很多优点,包括高性能、低成本(不用花钱)以及在众多软硬件平台之间的可移植性等。由于认识到GCC日渐重要,我在本版中介绍了更多与GCC相关的信息,包括如何使用GCC以及常见的GCC错误消息和警告。

·增加了对抽象数据类型的讨论。在第1版中,第19章重点讨论了C++。这部分内容现在看起来似乎作用不大,因为本书的读者可能已经学过C++、Java或者C#了。在本版中,对C++的介绍替换为讨论如何在C中建立抽象数据类型。

·扩展了国际化特性的内容。本版第25章更加详尽地讨论了C语言的国际化特性。重点扩展了Unicode字符集及编码。

·针对CPU和操作系统做了更新。当我编写本书第1版时,许多读者用的还是16位机和DOS操作系统,但现在情况不同了。在本版中,我把讨论的重点放在32位机和64位机上。尽管本版也介绍了Windows和Mac OS操作系统中影响C程序员的方面,但是针对Linux和其他UNIX版本的出现,我们的讨论更侧重于后一个系列的操作系统。

·更多的练习题和编程题。本书第1版包括311道习题,本版有将近500(准确地说是498)道习题,分为两组:练习题与编程题。

·练习题和编程题的答案。本书第1版的读者反馈最多的问题就是希望我提供习题的答案。针对读者的这一需求,我将大约三分之一的练习题和编程题的答案放到了网上,见knking.com/books/c2。这一特色对于那些没有选修相应大学课程但却需要检验自己工作的读者来说是非常有用的。提供了答案的练习题和编程题都用图标标记(“W”表示“此题在网站上有答案”)。

·有密码保护的教师网站。我为本版书建了一个新的教师资源网站(通过knking.com/books/c2访问),给出了其余练习题和编程题的答案以及大部分章节的PowerPoint讲义。教师可以通过cbook@knking.com与我联系。请使用您学校的邮件地址并给出一个可以访问贵系网站的链接,以便我核实您的身份。

此外,我在本版中对全书的文字和解释说明做了改进。这些改变所需的工作量很大,过程很辛苦:每句话都检查过并(在必要的时候)重新写过。

尽管本版改动很大,我仍然尽可能多地保持了原有的章节编号。尽管只有一章(最后一章)内容是全新的,但其他许多章都有新增的内容,少数原有章节的顺序也有所变动。本版删去了一个附录(C语言语法),但又新增了一个比较C99和C89的附录。

本版的目标与第1版一致。

·清晰易读,并尽可能带有趣味性。对普通读者来说,许多C语言的书籍都过于简洁。甚至某些C语言书籍不是编写得一塌糊涂,就是平淡无趣。我试图对C语言进行清晰、全面的讲解,并用适当的幽默来激发读者的阅读兴趣。

·适用于广泛的读者群。我假设本书的读者都至少有一点点编程经验,但不需要掌握某种具体的编程语言。我尽量减少“行话”并定义用到的每一个术语。同时,为了鼓励初学者,我还尝试将某些高级内容从基本主题中分离出来。

·有权威性,但不是学究气十足。为了避免对应该包含哪些内容、不应该包含哪些内容的武断决定,我尽量涵盖了所有C语言的特性和库函数。同时,为了避免给读者造成负担,我还忽略了一些不必要的细节。

·具备简单易学的组织结构。根据多年教授C语言的经验,我强调循序渐进地展示C语言特性的重要性。针对有一定难度的主题,我采用了螺旋式的介绍方法。也就是说,对于较难的主题先进行简要介绍,然后在后续章节中再进行一次或多次介绍,每次逐渐增加一些细节内容。本书的进度是经过深思熟虑的。每章都按照循序渐进的方式进行组织,并且前后内容由浅入深,相互呼应。对于大多数学生来说,这种循序渐进的方法是最合适的:既能避免产生厌倦,又能防止“信息超载”。

·深入探讨语言特性。我的目标不是仅描述语言的每个特性,并展示应用该特性的几个简单示例,而是尝试深入讲解每一个特性,并且探讨如何将其应用到实际问题中。

·强调编码风格。对每位C程序员来说,采用一种统一的编码风格是非常重要的。但是,与指定某种风格相比,我更愿意给出多种编码风格,让读者根据自己的喜好做出选择。因为了解多种编码风格对阅读别人的程序是很有帮助的(程序员经常要花费大量时间阅读别人的程序)。

·避免依赖任何特定的计算机、编译器或操作系统。C语言可以应用在许多平台上,所以我试图避免编写的程序依赖于任何特定的计算机、编译器或操作系统。所有程序都经过精心设计,可以移植到多种平台上去。

·用图示的方法阐明关键概念。我在书中加入了尽可能多的图,因为我认为图对于理解C语言的许多方面都是至关重要的。特别地,我尽可能地通过图来显示不同计算阶段的数据状态,以此来动态地展示算法。

本书最重要的目标之一就是通过一种“现代方法”来介绍C语言。我试图通过以下这些途径来实现这一目标。

·正确看待C语言。我没有把C语言看成是唯一值得学习的编程语言,而是把它作为众多有用语言中的一种进行介绍。我在书中提到了最适合用C语言编程的应用类型。此外,我还展示了如何扬长避短地使用C语言。

·强调C语言的标准版本。我尽可能少地关注C89标准之前的C语言,只是零星地提到了经典(K&R)C语言(Brian Kernighan和Dennis Ritchie所著的The C Programming Language第1版中所描述的1978版C语言)。附录C列出了C89和经典C之间的主要差异。

·揭穿神话。现今的编译器常常与过去的C语言基本假设不一致,我很乐于揭穿C语言的某些神话,并挑战一些存在了很久的C语言信条(例如,指针的算术运算一定比数组下标操作快)。我重新审查了C语言的旧惯例,保留了那些仍然有帮助的惯例。

·强调软件工程。我把C语言视为一种成熟的软件工程工具,着眼于如何运用C语言来处理大规模程序设计过程中产生的问题。本书强调程序要易读、可维护、可靠且容易移植,尤其重视信息隐藏。

·推迟介绍C语言的底层特性。虽然这些特性对于那些用C语言编写的系统来说非常有用,但现在它们已经不那么适用了,因为C语言的应用比以前广泛得多。本书没有像其他许多C语言书籍那样把这部分内容放在前面介绍,而是推迟到第20章再进行讲述。

·不再强调“手工优化”。许多书籍指导读者编写一些技巧性较强的代码,以获得程序效率的些许提高。如今优化的C语言编译器随处可见,这些编程技巧往往已经不必要了;事实上,它们反而会降低程序的运行效率。

每章的末尾都有一个“问与答”部分,汇集了与本章内容相关的问题及其答案。“问与答”部分包括以下一些内容。

·常见问题。我尽力回答了某些频繁出现在我的课堂里、其他书籍中及与C语言相关的新闻组里的问题。

·对一些难以理解的问题的进一步讨论和澄清。虽然具有多种编程语言经验的读者会满足于简明扼要的说明和少量的示例,但是缺乏经验的读者却需要更多的内容以帮助理解。

·非主流的问题。某些问题所引出的并不是所有读者都感兴趣的技术问题。

·某些对普通读者来说过于超前或深奥的内容。这类问题都用星号(*)进行了标记。好学且有一定编程经验的读者也许希望立刻深入研究这些问题,而另外一些读者则需要在首次阅读时跳过这部分内容。提示:这类问题往往引用后续章节的内容。

·C语言编译器之间的常见差异。我讨论了某些特定编译器所提供的一些频繁使用的(非标准)特性。

“问与答”部分中的某些问题与对应章中的具体内容直接相关,我们用一个专门的图标来标记这些具体内容,以提示读者有附加信息可用。

除了“问与答”部分,我还加入了许多有用的特色,其中很多都用简单而独特的图标做了标记。

·警告)警示读者一些常见的缺陷。C语言以其陷阱多而出名,要记录所有的陷阱非常困难。我试着挑选出了一些最常见或最重要的缺陷供大家参考。

·交叉引用(➤前言)提供一种类似超文本的能力来定位信息。多数引用指向后续章节中的内容,也有一些引用指向先前的内容供读者回顾。

·惯用法是C语言程序中常见的代码模式。它被标记出来以便于速查参考。

·可移植性技巧给出了编写不依赖于特定计算机、编译器或操作系统的程序所需的提示。

·附加说明包含一些严格来讲并不属于C语言的内容,但每位熟练的C程序员都应该知道。(下面的“源代码”给出了附加说明的示例。)

·附录提供有价值的参考资料信息。

选择程序示例并不是件轻松的工作。如果程序过于简洁和做作,那么读者将无法体会如何将这些特性应用于现实世界。另一方面,如果程序过于真实,那么它的要点将很容易被忽略在过多的细节中。我采取了折中方案。在首次介绍时,先通过小而简单的示例使概念清晰,然后再逐步建立完整的程序。我没有使用过长的程序,因为根据我个人的经验,教师没有时间介绍这些内容,而学生也不会有耐心去阅读。但是,我并没有忽视编写大规模程序时会出现的问题,相关内容在第15章和第19章中进行了详细的介绍。

我克制住了自己重新编写程序以利用C99特性的冲动,因为并不是每个读者都安装了C99编译器或愿意使用C99。但是,在有些程序中我仍然使用了C99的<stdbool.h>头文件,因为它定义了bool、true和false宏。如果你的编译器不支持<stdbool.h>,就需要自己定义这些宏。

本版的程序有些小的改变。现在main函数的格式在大多数情况下为int main (void) {...}。这一改变既反映了业界的惯例,又能够与C99兼容,C99要求每个函数都有一个显式的返回类型。

源代码

本书中所有程序的源代码都可以从knking.com/books/c2下载本书源代码也可以在图灵网站(www.turingbook.com)本书网页免费注册下载。——编者注。有关本书的更新、校正和最新消息也可以从这一网站获得。

本书是为大学本科阶段的C语言课程编写的教材。具有其他高级语言或汇编语言的编程经验会很有帮助,不过这些经验对于会用计算机的读者(我以前的一位编辑称他们为“熟练的初学者”)来说并不是必需的。

因为本书内容齐备、自成一体,并且既可用于学习又可作为参考,所以它非常适合作为其他一些课程的辅助读物,如数据结构、编译器设计、操作系统、计算机图形学、嵌入式系统及其他要用C语言进行项目设计的课程。“问与答”部分以及对实际问题的强调,使得本书对于培训班学员和自学C语言的人来说也很有吸引力。

本书分为4个部分。

·C语言的基本特性。第1章~第10章包含的C语言内容足以帮助读者编写出使用数组和函数的单文件程序。

·C语言的高级特性。第11章~第20章建立在前面各章内容的基础上,内容有一定的难度,深入介绍了指针、字符串、预处理器、结构、联合、枚举以及C语言的底层特性。此外,第15章和第19章提供了程序设计方面的指导。

·C语言标准库。第21章~第27章集中介绍C语言库——与编译器相关联的庞大函数集合。其中一部分内容适合讲解,但大部分材料更适合作为参考。

·参考资料。附录A给出了C语言运算符的完整列表。附录B描述了C99和C89之间的主要差别。附录C讨论了C89和经典C之间的差异。附录D按字母顺序列出了C89和C99标准库中的全部函数,并为每个函数给出了详尽的说明。附录E列出了ASCII字符集。还有一个带注解的参考文献列表为读者指明了其他的信息来源。

全面讲授C语言的课程应该按顺序覆盖前20章的内容,并根据需要增加第21章~第27章中的一些内容(其中讨论了文件输入/输出的第22章最为重要)。短期课程可以忽略以下内容而不失连贯性:8.3节(变长数组)、9.6节(递归)、12.4节(指针和多维数组)、14.5节(其他指令)、17.7节(指向函数的指针)、17.8节(受限指针)、17.9节(灵活数组成员)、18.6节(内联函数)、第19章(程序设计)、20.2节(结构中的位域)和20.3节(其他底层技术)。

作为一本教材,拥有多样化的精选习题显然是非常必要的。本版既有练习题(不需要写出完整程序的简短习题)又有编程题(需要编写或修改完整程序的习题)。

有些练习题的答案不是显而易见的(有人称其为“刁钻问题”)。因为C语言程序经常包含这类代码的大量案例,所以我认为有必要提供一些这样的练习,并用星号(*)进行了标注。对于有星号的习题一定要小心:要么格外小心,认真考虑,要么干脆绕开它。

为了使本书准确,我付出了极大的努力。然而,任何这种篇幅的书籍都不可避免地会有一些错误。如果读者发现了错误,请通过cbook@knking.com这个电子邮箱与我联系。我也同样期望听到读者的其他反馈,比如,你觉得哪些内容特别有用,哪些内容没什么用,希望添加哪些内容等。

首先,我要感谢本书的编辑,Norton出版社的Fred McFarland和Aaron Javsicas。本书的编辑工作最初由Fred负责,随后Aaron加入并付出了极大努力使本书得以完成。同时,还要感谢副主编Kim Yi、文字编辑Mary Kelly、生产经理Roy Tedoff和编辑助理Carly Fraser。

以下同事对本版的部分或全部书稿进行了审阅,在此致以诚挚的谢意:Markus Bussmann(多伦多大学)、Jim Clarke(多伦多大学)、Karen Reid(多伦多大学)和Peter Seebach(comp.lang.c.moderated新闻组的主持人)。其中需要特别提到的是Jim和Peter,他们的详细审阅使本版少了许多错误。再次感谢第1版书稿的审稿人(按姓氏字母排序):Susan Anderson-Freed、Manuel E. Bermudez、Lisa J. Brown、Steven C. Cater、Patrick Harrison、Brian Harvey、Henry H. Leitner、Darrell Long、Arthur B. Maccabe、Carolyn Rosner和Patrick Terry。

我收到了第1版读者反馈的许多有用的意见,感谢每一位花时间提意见的读者。佐治亚州立大学的学生和同事也向我反馈了不少有价值的意见。Ed Bullwinkel和他的妻子Nancy阅读了手稿的很多内容,在此我也要感谢他们。我还要特别感谢我的系主任Yi Pan,他非常支持我的这项工作。

感谢我的妻子Susan Cole一如既往地支持着我。还有我们的猫咪Dennis、Pounce和Tex,在完成本书的过程中,它们一直陪伴着我。有时,Pounce和Tex的争吵使我在深夜写作时仍能保持清醒。

最后,我还要感谢已故的Alan J. PerlisAlan J. Perlis(1922—1990)是计算机科学先驱,1966年首届图灵奖得主。——编者注。他的警句出现在本书每一章的开始。20世纪70年代中期我在耶鲁大学求学期间,曾有幸在Alan的指导下进行过短暂的学习。我想如果他知道自己的警句出现在一本C语言书中一定会非常高兴。