标题：程序设计实践（评注版）
图书信息
传世经典书丛  程序设计实践（评注版） 
（美）凯尼汉（Kernighan,B.W.）,（美）派克（Pike,R.）著
白乔评 注
ISBN 978-7-121-13458-6
2011年6月出版
定价：59.00元
16开
348页
内 容 简 介
The Practice of Programming针对程序设计过程中的风格、算法与数据结构、设计与实现、界面、除错、测试、性 能、可移植性、记法等各个方面，系统地讨论了一些常见问题和实用技巧。通过对本书的学习，读者可以快速掌握程序 设计的技巧及思想。
本评注版力邀国内资深专家执笔，在英文原著基础上增加中文点评与注释，旨在融合二者之长，既保留经典的原创 文字与味道，又以先行者的学研心得与实践感悟，对读者阅读与学习加以点拨、指明捷径。
评注者序
程序员的一生（武林大侠的人生貌似也是如此）往往都是在激情和 迷惘两种状态之间来回切换。当我们接触到一些新的东西（新的编程语 言、新的语法特性、新的协议工具，等等）时，就会急切地想去了解它、 掌握它，会坐在心爱的计算机前，废寝忘食、夜以继日地反复实践。可是 当我们的实践达到一定的程度时，往往又会陷入另外一种状态——觉得很 迷惘。一方面感觉自己学习的那点编程技巧已经足够，足以应付一些实际 的项目。但另一方面又会在实战过程中隐隐地感觉到有些问题存在：代码 写得总是有点丑陋、运行效率总是比想象中的要慢一些、和其他程序模块 交互起来总是存在着一些别扭、一些莫名其妙的错误总是会在一些不恰 当的时候出现，你无法自信地告诉用户你的程序真的很棒，如果将它从 Windows下移植到Linux下，你甚至不知道会有什么问题……你明明意识到 了这些问题，却苦于无法去改进，教科书中的每一句话早已烂熟于心，却 丝毫找不到问题的答案，和程序员前辈讨论的时候，也只会得到一些只言 片语，总不 能找到一套系统的修炼之道——针对这种情况，你要做的就是 寻找一些能针对实际问题对症下药的读本，而The Practice of Programming 就是这样的一本书。
The Practice of Programming 由Kernighan和Pike编著，成书于上个世纪
（1999年）。年代看似有些久远，但该书讨论的是一些很难过时的话题， 就像我们今天还在讨论2000多年前“孔孟之道”和“孙子兵法”一样，12 年后的今天，我们再次读起The Practice of Programming ，仍没有感觉到一 丝腐朽的气味。从内容上看，该书几乎囊括了程序员深有感触的各种问题，对于很多彷徨的程序员来说，这绝对是一贴切中要害的良药。全书主 要采用C++/Java作为范例语言，但丝毫不影响其他语言的爱好者以此作为 升级读本。
全书分成9章，分别是：
Chapter 1: Style（风格）
指导你如何写出优雅的代码，而不是乱写一气。
Chapter 2: Algorithms and Data Structures（算法与数据结构） 总结一下各种基本的算法和数据结构。
Chapter 3: Design and Implementation（设计与实现） 紧接第2章，用5种不同的语言讨论具体的程序实现。
Chapter 4: Interfaces（界面） 展示了用户和程序之间、程序模块之间应有的界面设计。
Chapter 5: Debugging（除错） 系统地讨论了除错的各种策略和技巧。
Chapter 6: Testing（测试） 指导如何手动地、自动地进行程序的测试，以保证程序能正常工作。
Chapter 7: Performance（性能） 循序渐进，演示如何通过不断的重构优化程序的性能。
Chapter 8: Portability（可移植性） 讨论提高程序的可移植性所关注的地方和可用的技巧。
Chapter 9: Notation（记法） 讨论如何采用一些有用的记法简化程序，将数据和逻辑用合适的记法表达出来。
很 多 程 序 员 很 享 受 阅 读 英 文 原 著 ， 从 某 种 角 度 来 说 ， 英 文 更 能 确 切表达计算机世界的概念，如同样是“注解”、“注释”，comment和 a n n o t a t i o n 在程序设计世界中就具有不同的含义。有些词汇一旦做了翻 译，如object、list、map、foreach等，反倒会让国内读者感受到莫名的障 碍，程序员彼此交流时，有的时候就干脆直接采用英文说法。再如一些 缩写，如CSV格式，它来源于Comma-Separated Values（“以逗号分隔的 值”），原义极其朴素，但缩写成CSV就显得太过高深。同样的例子还有 internationalization和localization，它们常被缩写成I18N和L10N，如果抛开英文原词，就会多一些神秘感。再如：“Abort, Retry, Fail?”这是操作系统 中常常打印出来的一句原话，如果再翻译成中文，就失去了原有上下文的 意味。阅读英文原版的另外一个好处，还在于当我们看到memset()函数时 就会联想到memory set，看到strcpy()函数时就会联想到string copy。
The Practice of Programming 用语浅显易懂，深入浅出，但由于文化的 差异，在为避免读者理解上的困难，有些地方我们在保留原汁原味的同 时，还增加了一些本地化的注解。注解的比例大概为20%左右，通过附上 些亲切的中文用以强调、扩展作者提出的某些概念和观点，以避免读者迷 失在英文词句的汪洋大海中，一去不复返。
在评注过程中，由于学识有限，为避免一面之辞，评注者大量参考了
C++ ISO标准、互联网资料与国内的一些计算机教材，也包括裘宗燕老师
2007年针对The Practice of Programming 的译本，力求在术语使用和观点解 释上，不会给国内程序员造成任何误解。最后感谢电子工业出版社的张春
雨先生及其他同事，是你们的辛勤劳动促成了这本评注版得以与广大程序 员见面。
正如作者所强调的，程序设计并没有编码那么简单，它是一项高技巧 的脑力劳动。悲惨的是，程序设计被很多的人“演绎”成、也被更多的人 “曲解”成毫无技巧乐趣可言的体力劳动！我们看到千千万万的准“程序 员”，进入某个软件工厂，仅仅通过几个月的培训，就被制造成能够“胜 任”的程序员，在这里，他们被灌输的仅仅是规则的遵守和他人代码的机 械模仿，留给他们的思考和设计的空间极其有限。失去了思维的权利的程 序员，在随后的人生中不得不在各种各样的岗位上按照他人的意愿做着一 次又一次的重复的机械式的工作，很多的程序员也因为兴趣问题放弃了程 序设计，转向其他的职业生涯。殊不知，程序设计的世界是其乐无穷的， 是无止境的，它需要永久的激情 和持续的实践积累。评注者真心期望读者 朋友们能从阅读本书的过程中得到收获，得到心灵上的升华。唯有那些有 思想的程序员，方能写出有灵魂的代码。
2011.4 北京
前 言
你是否曾经……
花费了太多的时间去对一个本来就错误的算法做编码实现？ 使用了一个超级复杂的数据结构？ 测试一个程序，却忽视了其中极其明显的问题？ 花整天的工夫来查找一个其实只需要5分钟的bug？ 需要让一个程序运行速度快3倍并且要使用更少的内存？ 耗费心机地把一个程序从工作站移到PC上，或者反过来？ 试图对其他人写的某个程序做个适度的修改？ 因为根本无法理解一个程序，你干脆就重写了它？ 这是不是很有趣？
这样的事情每时每刻都在程序员身上发生。处理这些事情往往要比想 象的困难得多。其原因就在于：常规的计算机科学和程序设计课程都不大 关注那些诸如测试、除错、可移植性、性能、设计以及风格等程序设计实 践方面的话题。大部分程序员可能会随着自己经验的增长无计划地学到一 些东西，但也有些人，他们始终都不会学到这些东西。
当人们面对着那些极度复杂的界面、不断变化的工具、语言与系统， 以及来自于其他事情的无情压力的时候，往往很容易忘掉某些基本的法 则，即简单、清晰与普适，事实上正是这些法则构成好的软件的基础。人 们也会忽略工具和记法的效用，实际上，利用它们可以让计算机自动构造 出某些软件，从而让计算机专心致力于真正的程序设计。
本书提出的一些方法，正是基于这些适用于计算的各个层次的潜在 的、互相关联的法则而成。这些方法包括：简单化，它能使程序简短且易于管理；清晰化，它保证无论是人还是机器都能很好地理解程序；普适 化，它意味着程序在很多种情形下都能很好地工作，当新的情况出现时， 也能很好地调整；自动化，让计算机帮我们做一些工作，从而使我们从琐 事中解脱出来。通过考察各种不同语言中的程序设计，我们从算法、数据 结构到设计、调试、除错、测试和性能改进，阐述了一些具有普遍意义 的，且独立于语言、操作系统和编程范式的工程化概念。
本书提炼于我们多年的软件开发和维护、程序设计课程的教学以及 与各种各样的程序员共同工作的经历。我们希望与大家共享这其中的经验 和教训，从我们的实践中有所感悟。其中向各层次的程序员给出了一些建 议，以使大家的工作变得更加娴熟、高效。
本书适用于不同类型的读者。如果你是一名学生，已经上过一两门程 序设计课程，希望成为一个更好的程序员，这本书将为你展开一些在课堂 里只能做有限的讨论的某些话题。如果你把编程作为自己的一部分任务， 而非你工作的全部，这些 信息将帮助你快速有效地写出程序。如果你是职 业程序员，在学校期间没有学过这些内容，或者希望借此温习一下，或者 你是个软件管理者，希望你的员工得到指导而有所进步，那么这里提供的 材料都会是很有价值的。
我们希望这里给出的建议能够帮助人们写出更好的程序。对你唯一有 要求的前提条件就是，你已经做过一些程序设计，而且最好是用C、C++ 或者Java语言。当然，已有的经验越多，就会越容易——但也无法做到让 你在21天内从新手变成专家
较之那些经常用Windows和Macintosh的程 序员，UNIX和Linux的程序员可能会觉得本书的一些例子更为熟悉一点。 但是不管你来自哪个环境，程序员都该找点东西，让生活过得更为舒坦一 些。
相反，在这之前，市场上充斥着各种《21天教程》，名字比较诱人，但估计 连作者自己也不敢相信谁能够真的在21天内学完这些东西。
本书分成9章，每一章关注程序设计实践的一个方面。 第1章讨论程序设计风格。好的风格对于好的程序设计极其重要，因此
我们放在最前面讨论。书写良好的程序总比乱写一气的程序好——它们包
含的错误更少，更容易除错，也更易于修改。因此从一开始就注意风格这 尤为重要。这一章还介绍了良好程序设计的一个重要主题，即尽量采用编 程语言的习惯用法。
第2章讨论算法和数据结构，这是计算机科学必修课的核心内容，也是 程序设计课程的重要组成部分。鉴于许多读者已经熟悉了这方面的内容， 我们对那些几乎出现在每个程序里的算法和数据结构做了一个简单的回 顾。更复杂的算法和数据结构通常都是由这些基本东西构建起来的，因此 需要掌握好这些基础。
第3章讨论了一个小程序的设计，通过该程序展示实战环境中算法与数 据结构的问题。该程序采用5种不同的语言实现，通过比较这些不同的实现 版本，我们不难发现相同的数据结构如何在不同语言里来进行处理，以及 不同语言在表达能力和性能方面之间的差异等。
用户和程序之间、程序不同部分之间的界面是程序设计的基本问题。 软件的成功很大程度上取决于良好的界面设计和实现。第4章展示了一个 小函数库的不断演化过程，该函数用以解析一种通用的数据格式。虽然这 个例子很小，但却能展现出界面设计的许多重要问题，诸如抽象、信息隐 蔽、资源管理和错误处理，等等。
虽然我们总希望一次就能把程序写正确，但是，bug以及由之带来的除 错，实际上是无法避免的。第5章讨论了一些系统化的、有效的除错策略与 技巧。在这些话题中，读者可以看到常见的bug，以及数值规律的重要性： 除错输出中某些模式性的东西往往能告诉我们哪里出了问题。
测试的目的就是试图给出一种合理的保证，以证明一个程序是在正常 地工作，或者说它经过改造之后还是正常的。第6章强调如何通过手工和机 械的方式进行系统化的测试。边界条件测试往往布置在那些潜在的薄弱点 上。机械化和测试台会使得测试事半功倍。压力测试提供了一种非常人能 用的方法进行的另一类测试，它能找出另外一些错误。
今天的计算机，其速度已经足够快了，编译器做得也是足够好的，因 此，许多程序在写出之时就已经足够快。但是，另一些程序还是慢，或者 耗费了太多的内存，或者二者兼而有之。第7章给出了一种有序的方法， 以使程序能更有效地使用资源，并在提高效率的同时仍然保持原先的正确 性。
第 8章涵盖了可移植性问题。对于成功的程序来说，当环境发生了变 化，或者需要移植到新的系统、新的硬件，甚至新的国家时，它们仍会正 常工作足够长的时间。可移植性的目标就是减少程序移植到新环境下所必 需的变动，藉此降低程序的维护成本。
今天的计算语言已经相当丰富了，除了我们常用来做程序设计的通用 语言，还有许多应用在较窄领域中的专用语言。第9章给出了一些例子，说 明在计算领域里记法的重要性，并说明如何采用它来简化程序、指导程序 实现，或者帮助我们编写那种能够写程序的程序。
由于讨论到程序设计，我们不得不贴出很多代码。其中大部分的例子 是为本书特意准备的，也有些小例子改编自其他来源。为了把这些代码写 好，我们花了很大的功夫，并且在半打的操作系统上测试过这些代码的机 器码。
本书里的大部分程序都是基于C语言的，也有一些用C++或Java写的程 序例子，还有关于脚本语言的简单展开。C和C++在低层次上几乎是完全一 样的，因此我们给出的C程序往往也是合法的C++程序。C++和Java作为C 语言的直系后代，它们继承的远不止一些语法，更多的 是其效率和表达能 力，同时增加了更为丰富的类型和函数库。
在我们自己的工作中，经常会用到这三种语言，当然也有一些其他 的语言。语言的选择取决于：操作系统最好用某种高效的、不受限制的语 言，如C或者C++；快速原型通常采用某个命令解释器或者脚本语言，比如 Awk或Perl；对于用户界面，Visual Basic或Tcl/Tk，还有Java，都是有力的 候选者。
对于我们的例子，语言的选择上还有一个重要的教学因素。正如没有 一种语言能够解决所有问题一样，也没有哪一种语言能够很好地演绎出所 有的话题。高级语言往往暗含了某种设计策略的选择。如果用低级语言， 我们将能全面地讨论问题的各种解答，揭示其中更多的细节，讨论的效果 会更好。经验证明，即使是在使用高级语言的功能时，了解它们与低级问 题之间的关系也是很有意义的。如果没有这种深入的观察，我们就很容易 陷入到性能或者某种奇怪的行为当中。因此，我们常常采用C语言作为例 子，即便 是实际环境下我们会采用其他的语言。
不过，本书大部分的内容其教程还是独立于任何特定程序设计语言的。 数据结构的选择受到所使用语言的影响，在有些语言里这种选择会少些，在 另外一些语言里则会多些。但是如何来选择，其方法其实是一样的。在不同 语言里，如何做测试和排错都会有所差异，但其策略和技巧也是类似的。一 言以蔽之，改进程序效率的大部分技术都可应用到任何语言。
作为一名程序员，不管是采用什么语言写程序，你的工作都是如何 利用手头的工具，尽可能地把事情做好。一个优秀的程序员可以克服由 一种不够强大的语言或者一个简陋的操作系统所带来的缺陷，反之，即 使是一个超级棒的程序设计环境，也没法帮一个糟糕的程序员的忙。我 们希望无论你现在的经验和技巧如何，这本书都将帮助你更好地做程序 设计并享受之。
衷心感谢所有那些读过书稿并给予帮助性的建议的朋友和同事。Jon Bentley，Russ Cox，John Lakos，John Linderman，Peter Memishian，lan 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，他们对手稿的各个方面提出了极有价值的意见。我们也感 谢A1 Aho，Ken Arnold，Chuck Bigelow，Joshua Bloch，Bill Coughran， Bob Flandrena，Renee French，Mark Kernighan， Andy Koenig，Sape Mullender，Evi Nemeth，Marty Rabinowitz，Mark V. Shaney，Bjarne Stroustrup， Ken Thompson和Phil Wadler，感谢你们给予的忠告和成熟的建议，谢谢你们。
Brian W. Kernighan
Rob Pike
目 录
前言（新增批注共1条）
Chapter 1: Style（新增批注共46条） xii
1
1.1　Names 3
1.2　Expressions and Statements 7
1.3　Consistency and Idioms 13
1.4　Function Macros 21
1.5　Magic Numbers 23
1.6　Comments 28
1.7　Why Bother? 35
Chapter 2: Algorithms and Data Structures（新增批注共29条） 
37
2.1　Searching 38
2.2　Sorting 41
2.3　Libraries 44
2.4　A Java Quicksort 48
2.5　O-Notation 52
2.6　Growing Arrays 54
2.7　Lists 57
2.8　Trees 64
2.9　Hash Tables 70
2.10　Summary 74
Chapter 3: Design and Implementation（新增批注共12条） 
76
3.1　The Markov Chain Algorithm 77
3.2　Data Structure Alternatives 79
3.3　Building the Data Structure in C 81
3.4　Generating Output 85
3.5　Java 89
3.6　C++ 93
3.7　Awk andPerl 97
3.8　Performance 99
3.9　Lessons 101
Chapter 4: Interfaces（新增批注共20条） 104
4.1　Comma-Separated Values 105
4.2　A Prototype Library 107
4.3　A Library for Others 111
4.4　A C++ Implementation 121
4.5　Interface Principles 126
4.6　Resource Management 130
4.7　Abort, Retry, Fail? 135
4.8　User Interfaces 140
Chapter 5: Debugging（新增批注共28条） 144
5.1　Debuggers 146
5.2　Good Clues, Easy Bugs 148
5.3　No Clues, Hard Bugs 153
5.4　Last Resorts 160
5.5　Non-reproducible Bugs 164
5.6　Debugging Tools 166
5.7　Other People’s Bugs 169
5.8　Summary 171
Chapter 6: Testing（新增批注共28条） 173
6.1　 Test as You Write the Code 174
6.2　Systematic Testing 181
6.3　Test Automation 186
6.4　Test Scaffolds 189
6.5　Stress Tests 193
6.6　Tips for Testing 197
6.7　Who Does the Testing? 199
6.8　Testing the Markov Program 200
6.9　Summary 202
Chapter 7: Performance（新增批注共30条） 204
7.1　A Bottleneck 205
7.2　Timing and Profiling  
211
7.3　Strategies for Speed  217
7.4　Tuning the Code  221
7.5　Space Efficiency  228
7.6　Estimation  231
7.7　Summary  234
Chapter 8: Portability（新增批注共30条） 236
8.1　Language 237
8.2　Headers and Libraries 245
8.3　Program Organization 247
8.4　Isolation 252
8.5　Data Exchange 254
8.6　Byte Order 256
8.7　Portability and Upgrade 259
8.8　Internationalization 262
8.9　Summary 266
Chapter 9: Notation（新增批注共13条） 269
9.1　Formatting Data 270
9.2　Regular Expressions 278
9.3　Programmable Tools 286
9.4　Interpreters, Compilers, and Virtual Machines 289
9.5　Programs that Write Programs 296
9.6　Using Macros to Generate Code 300
9.7　Compiling on the Fly 301
Epilogue 
308
Appendix: Collected Rules 311
Index 315