你是否曾:
浪费了许多时间去对一个错误算法做编码?
使用了一个数据结构,实际上它是过于复杂了?
测试一个程序而忽略了其中最简单的问题?
化了一整天功夫查找一个错误,实际上你应该在5分钟里就找到它?
需要让一个程序运行得快3倍而且只使用一半的存储?
费劲地把一个工作站上的程序移到PC上,或者相反?
试图对其他人写的某个程序做最简单的修改?
重写一个程序,原因是你根本无法理解它?
这些事都很有趣吗?
编程序的人每时每刻都遇到这些事情。但在处理时发生的情况却很不令人满意,通常都比实际上应该的情况更糟。产生这种状况的原因是,常规的计算机科学和程序设计课程都不大关注那些程序设计实践方面的论题,如测试、排错、可移植性、性能、设计可能性和选择、程序设计风格等等。许多程序员随着自己经验的增长慢慢地、毫无计划地学到了一些东西,也有些人始终都没有学好。
在这个庞大的世界里,人们面临的是错综复杂、相互联系和处于不断变化中的工具、语言和系统,以及对任何东西都提出更多要求的无情压力。这种情况下,人们很容易忘掉那些最基本的原则:简单性、清晰性、普遍性。而正是这些东西,实际上是构成好软件的基石。人们也可能忽略工具和描述法的价值。实际上,利用它们可能机械性地构造出某些软件,从而让计算机为其本身的程序设计做一些事情。
在这本书里,我们采用的方式是讨论一些基本的,互相关联的原则,它们可以用到计算的各个层次中。这些原则包括:简单性,它能使程序短而容易处理;清晰性,它保证程序容易理解,无论是对人还是对机器;普遍性,它意味着程序在很广泛的情形下都能工作得很好,也容易做修改以适应新出现的情况;自动化,借助计算机帮助我们做一些工作,使人能够摆脱许多平凡琐碎的事项。通过考察不同语言中的一些程序设计例子,从算法和数据结构,到设计、调试、排错和性能改进,我们能展示出许多具有普遍意义的工程概念,它们是独立于具体语言、操作系统和编程范型的。
本书植根于我们多年来开发和维护大量的软件,多年讲授程序设计课程,多年与各种各样的程序人员共同工作中的经历。我们希望与人共享这些关于程序设计实践问题的经验和教训,从我们的经验中传达一些深层的认识,提出一些方法和建议,帮助各种水平的程序员变得更加熟练,工作更有效率。
我们的书是为许多不同类型的读者写的。如果你是学生,已经上过一两门程序设计课程,希望成为一个更好的程序员,这本书将给你展开某些问题,而这些问题在你自己的课程里没有足够的时间讨论。如果你把写程序作为自己的一部分任务,目的是为了完成其他工作而不是做程序本身,这里的信息将帮助你更有效地编写程序。如果你是职业程序员,在学校期间没有学过这些内容,或者希望重新温习一下;或者你是个软件管理者,希望指导你的工作人员向正确方向发展,这里的材料都会非常有价值。
我们希望这里提出的建议能帮助人们写出更好的程序。读这本书的惟一先决条件,是你已经做过一些程序设计,最好是用C、C++或者Java。当然,已有的经验越多,有关内容对你来说就越容易。不可能有任何东西能帮你在21天内从新手变成专家。对于这里的某些例子,Unix和Linux程序员可能比经常用Windows和Macintosh的程序员更熟悉一点。但是我们相信,无论你来自哪个环境,都一定能在这里发现一些东西,它们可能使你的生活变得更容易些。
本书全部内容被组织为9章,每章集中讨论程序设计实践的一个重要方面。
第1章讨论程序设计风格。好的风格对于好的程序设计是如此重要,所以我们选择在最前面讨论它。书写良好的程序比胡乱写出的程序好,它们包含的错误更少,更容易排错、容易修改。所以,最重要的就是从一开始就注意风格。在这章里还提出了好程序设计的一个重要论点:应该尽量采用对所用语言最合适的习惯用法。
算法和数据结构是第2章的论题,这是计算机科学教育计划的核心,也是程序设计课程的重要组成部分。由于许多读者已经熟悉了这方面情况,本书中的处理方式就是对那些几乎出现在每个程序里的算法和数据结构做一个简单的总结。更复杂的算法和数据结构通常都是由这些基本东西建构起来的,因此需要掌握这些最基本的东西。
第3章讨论一个小程序的设计,以便在实际工作中展示讨论与算法和数据结构有关的各种题。这个程序用5种不同语言实现,通过对各种不同实现的比较,说明了同样的数据结构如何在不同语言里处理,语言之间在表达能力和性能方面的变化情况等。
用户和程序之间、程序不同部分之间的界面是程序设计的一个最基本问题。软件的成功很大程度上取决于这些界面设计和实现。第4章用一个小函数库作为例子,展示了界面的演化过程,这个库用于分析处理一种广泛使用的数据格式。虽然这个例子很小,但却显示出界面设计的许多重要问题:抽象、信息隐蔽、资源管理、错误处理等等。
虽然我们总是希望第一次就能把程序写正确,但是,程序错误及相应排错过程实际上是无法避免的。第5章讨论了系统化地、有效地排除程序错误的各种策略和技术。有关论题中包括常见错误的标志、数值特性的重要性等等。程序输出中模式性的东西常常能告诉我们问题究竟出在哪里。
测试的目的就是企图给出一种合理的保证,说明一个程序能正常工作,或者说明它经过改造之后仍然是正确的。第6章强调的是如何通过手工和机械的方式进行系统化的程序测试。边界条件测试针对潜在的薄弱点进行仔细检查,机械化和测试台测试使人可以比较容易地以合理代价完成昂贵的测试,应力测试提供与典型使用不同的另一类测试,它可能搜寻出另外一些种类的错误。
计算机的速度已是风驰电掣,编译器已经做得如此之好,因此,许多程序在写出来之时就已经足够快了。但是,另有一些程序则实在是太慢,或者是使用了太多的存储,甚至同时存在这两方面的问题。第7章给出了一种有序方法处理这些问题,设法改造程序,使之能更有效地使用资源,并使它在提高效率的同时仍然是正确和有效的。
第8章讨论可移植性问题。一个成功的程序可能在环境不断变化的情况下使用相当长时间,或者需要搬到新的系统、新的硬件、甚至新的国家去。可移植性的目标就是设法减少程序的维护,使程序在移植到新环境时要做的改动达到最小。
计算是一个语言丰富的领域,在这里,不止有我们常用来做程序设计的通用语言,还有许多集中应用在较窄领域中的专门语言。第9章给出了一些例子,说明在计算领域里记法的重要性。这里还想说明怎样利用这方面的东西来简化程序,指导程序设计,或者帮助我们编写那种能够写程序的程序。
由于要讨论的是程序设计,我们不得不给出许多代码。这里的大部分例子是为本书特别写出来的,也有些小例子取自其他来源。为把这些代码写好,我们已经做了很大努力,而且在半打的系统上的测试过这些代码,直接采用机器可读的形式。关于《程序设计实践》这本书的更多信息可以在下面网址找到:
我们衷心地感谢所有那些朋友和同事,他们读过本书的草稿,给了我们许多很有帮助的建议。Jon Bentley,Russ Cox,John Lakos,John Linderman,Peter Memishian,Ian Lance Taylor,Howard Trickey,和Chris Van Wyk极其认真地通读过本书的手稿,有人还读过不止一遍。我们感谢Tom Cargill,Chris Cleeland,Steve Dewhurst,Eric Grosse,Andrew Herron,Gerard Holzmann,Doug McIlroy,Paul McNamee,Peter Nelson,Dennis Ritchie,Rich Stevens,Tom Szymanski,Kentaro Toyama,John Wait,Daniel C. Wang,Peter Weinberger,Margaret Wright和Cliff Young对手稿各个方面提出的极有价值的意见。我们也感谢Al Aho,Ken Arnold,Chuck Bigelow,Joshua Bloch,Bill Coughran,Bob Flandrena,Renée Frence,Mark Kernighan,Andy Koenig,Sape Mullender,Evi Nemeth,Marty Rabinowitz,Mark V. Shaney,Bjarne Stroustrup,Ken Thompson和Phil Wadler等的好建议和经过深思熟虑的提议。谢谢你们大家。
Brian W, Kernighan
Rob Pike