代码阅读

出版时间:2012-8  出版社:电子工业出版社  作者:季奥米季斯·斯宾耐立思(Diomidis Spinellis)  页数:416  译者:左飞,吴跃,杨宁  
Tag标签:无  

前言

  原作者中文版序  中国是首个将我的“开源视角”系列作品再版的国家。这可能有很多原因,而其中一个特别吸引人的原因与孔子的著作有关,他在《论语》中广泛地强调了学习研究的重要性。回顾之前撰写《代码阅读》和《代码质量》的历程,我了解到,实际上,我鼓励了那些从事开发工作的同事和学生借助研究学习软件代码来提升他们自身的知识和技能,这正是我遵循孔子金玉良言的一种方式。  《代码阅读》一书阐述了开发者应当如何阅读已有的代码。关于为何要进行代码阅读,不少人也给出了许多现实中的原因:修正问题、添加特性、寻找有用的片段,或者作为你所在机构质量控制流程的一部分对其进行复查。然而,进行代码阅读最重要的原因其实是从中学习。从已有的高质量代码中,我们可以学习如何将严谨的代码风格应用于实践,如何编写有用的注释,如何编排代码以方便他人读懂,如何选择有意义的标识符,以及如何将复杂的代码组织为可管控的部分。另外,通过研究代码,我们还可以学习到新的算法、API及架构。简而言之,阅读代码可以帮助我们成为更加优秀的程序员。  《代码质量》一书,退后一步,方为大观。当代码被组装为程序时将会产生所谓的聚现属性:可靠性、安全性、CPU利用率、空间占用率、可移植性及可维护性。尽管这些属性可能看起来抽象且难以约束,但是诸多成功的经验表明,借助研究专家级的代码,我们可以学习到许多极好的提升代码质量的方法。我们可以从中发现严谨的错误测试、安全证书的保守处理、高效的算法、灵巧的抽象技术及应用于实践中的基本设计模式。简而言之,这将有助于我们成为极其优秀的程序员。  伟大的哲学家孔子曾经提过:“学而不思则罔,思而不学则殆”。因此我推荐大家主动花些时间来研究已有的代码并从中予以学习。  Diomidis Spinellis  2011年9月于雅典  推 荐 序  今年恰逢“十二五”开局之年,在全球软件技术和产业格局孕育重大调整之际,我国软件产业也在工业化、信息化“两化融合”的大背景下迎来了又一个快速发展的新阶段,这其中机遇与挑战并存。当下软件和信息服务业市场的规模不断扩大,物联网作为又一个万亿元级别的产业将产生千亿元级别的服务外包。预计到2020年,全球潜在的服务外包市场需求将达到1.65~1.8万亿美元,大力发展软件业及信息服务业将成为各国抓住新机遇、全面深度参与全球化、提升软件产业技术力量的重要途径。目前我国软件产业规模虽已过万亿元,但在核心技术、基础软件等方面仍有很大发展空间。  高素质人才的储备是推进产业健康快速发展的根本保证。高端软件人才的大量持续涌现,关键在于教育,这其中高校无疑要发挥重要的作用。我们高校软件教育者既要继续贯彻党的教育理念,进一步深化我国高级软件人才培养体系的发展进程;同时又要看到我国与欧美等高水平软件人才教育国家之间的距离,师夷长技,以求在全球化浪潮中谋得一席之地。作为一位优秀的软件教育者,Diomidis Spinellis教授的某些理念无疑是非常值得我们学习和借鉴的。他以人类学习自然语言的认知规律为出发点,独辟新径,强调借助代码阅读来提高编程能力。目前这一思潮也已逐渐由欧美向我国渗透。  代码阅读是每一个软件从业人员经常进行的活动,其重要性对于每一个开发者不言而喻,但人们更多的是在本着修改前人代码而进行此项活动的,换言之,仅仅使用了代码阅读的工作属性,而未见开发其学习属性。其实,代码阅读还帮助人们完成了“观察-模仿-创造”这样一个过程的初始阶段。南朝刘勰《文心雕龙》里讲“观千剑而后识器”,与之类似,清乾隆年间蘅塘退士还说“熟读唐诗三百首,不会作诗也会吟”,这都是强调了观察对于之后创造的重要性(巧合的是写诗和写代码的观察都是借助阅读来完成的)。  令人遗憾的是,一直以来,许多人都认为阅读代码不是件容易的事情,不仅不容易,很多时候还非常枯燥;即使是自己写的代码,有时隔一段时间再回顾也会不知所云。很多人在自己的编码生涯中都或多或少有过一些阅读代码的经历,有自己的一些方法,但也仅仅是一些个人实践而已,缺乏对整体的把握,经常是只见树木不见森林(很多时候仅仅能看到一小部分树木)。为了在学习的过程中少走一些弯路,业界代码阅读与质量提升方面的开宗明义之作--Diomidis Spinellis教授所撰写的两部经典之作《代码阅读》和《代码质量》无疑是推荐给每位从业人员的理想读物。这两部曾荣获美国Jolt软件开发震撼大奖的作品,影响了一代程序员,是相关领域中的经典名作。  我阅读了两部书的译稿,并非常欣喜地将它们推荐给每一位读者。该书译者和编辑们严谨、认真的工作使得本版最大程度地还原了作者的原意,相信经由他们的辛勤努力,必将能为广大读者献上一道惊艳的佳作。  欧阳修说:“立身以立学为先,立学以读书为本。”衷心希望广大读者借由本书立学解惑,提升自我。  李战怀  于2011岁末  译 者 序  由国际知名的Addison-Wesley出版社推出的“高效软件开发系列”丛书为现代软件开发的方方面面提供了专业的建议和意见。收录在该系列中的书籍本本都是技术方面声名卓著的佳作,这些书籍的作者在创作时煞费苦心,力求作品篇幅适中、易于阅读,同时保证作品的价值能够历久弥新,不会随时光的流逝而渐显黯淡。系列中的每一本都描述了一项软件开发的核心话题,这些内容可能是业界专家们在开发中始终秉承的,也可能是需要本书的读者们引以为戒的,而之所以这样做,目的只有一个,就是要创造出最杰出的软件。  希腊教授Diomidis Spinellis的两部著作《代码阅读》和《代码质量》均收录在此系列中。其中,本书作为该领域的开山之作,曾荣获美国第14届“软件开发”年度震撼大奖。在IT产业蓬勃发展的今日,电子工业出版社顺应时代发展和广大读者希冀,隆重推出了《Jolt获奖系列》书丛,《代码阅读》和《代码质量》再次被收入其中。经典之作《代码阅读》得以拨云开雾,同广大中国读者见面。  当今世界,互联网迅猛发展,开源软件大行其道,海量的优质代码犹如一座丰富的宝藏正热切地等待着每一位开发人员去发掘,去阅读。正如本书英文原版序言的作者Dave Thomas所说的那样:“没有哪位伟大的小说家从未读过其他人的著作,也没有哪位伟大的画家从来没有研究过他人的画作。”阅读代码显然是每个软件开发人员都必须做的事情。一方面,可能是出于向优秀范例程序学习的目的,另一方面,也可能是出于代码复用的目的。于是,人们总会出于这样或那样的原因去阅读代码(有可能是别人的,也有可能是自己写的)。然而,长久以来,“如何正确地阅读代码,实现去粗取精?如何高效地阅读代码,做到得心应手?”始终没有一套完备的指导法则。  直到本书出现,我们才豁然开朗,原来代码阅读也是有章法可依的,也是一门学问。众所周知,本书是第一部专门将代码阅读作为一项独立活动加以论述的书籍,即使在其出版多年之后的今天,也依然是该领域的扛鼎之作。正如本书作者所说的那样:“代码阅读应该得到正确地训练,并用做提高编程能力的一种方法。”值得注意的是,目前,在国外先进的计算机教育体系中,代码阅读课程、活动和实践已纳入到相应的正规课程安排之中。  在中国,各大技术论坛、专业网站也为广大从业人员和程序设计爱好者提供了大量可交流的代码资源。作为译者,我们真诚地将该书推介给国内的读者,希望借由本书,可以推动开源运动,使更多人获益,也可以推动代码共享与阅读的方法与实践,提高国人学习编程的效率,以期获得事半功倍的效果。  说到学习编程,我不禁想到了最近网上热炒的“蹭课哥”。据悉,来自山东菏泽,现年27岁的农村青年贾作胜,原本高中毕业后,由于家庭经济困难等原因,未能如愿继续深造。后来,进城打工的他成为了清华大学的一名普通的保安员。业余时间,他一直刻苦学习,还常到教室旁听各种课程和名家讲座。天道酬勤,今年这位往日的“蹭课哥”,凭借旁听和自学,考上了山东师范大学。同样是保安,我又不禁想到了一个我身边的真实的例子。这个故事我不止一次讲起,还曾经在我的一本书的序言里引用过。若干年前,我所在公司楼下有一位与众不同的小保安。说他与众不同是因为当时经常能看到他捧着一本厚厚的《Java编程思想》之类的书在啃读,这种争分夺秒、刻苦学习的形象当时给我们很多人留下了深刻的印象。功夫不负有心人,这位保安后来顺利转型,成为了一名软件工程师。  无论是过去还是现在,都有如此之多的形形色色的人因为各种原因前仆后继地在努力钻研编程。我也曾经在文章中指出,研读他人的代码是学习编程的道路上,除动手实践外最重要也最必要的学习方法之一。本书的读者可能已经在行业内摸爬滚打多年,也可能初出茅庐,可能是高等院校计算机相关专业的学生,也可能只是某个角落里默默奋发的小保安。无论你是其中的哪一个,我都真心地希望本书能够在你成长为编程达人的道路上助一臂之力。我希望能够有更多像小保安一样的人得以华丽转身。若能如此,我心足矣。  怀揣着这样的心情,我们始终秉持着一丝不苟的态度,力求把经典之作原汁原味地带给中国读者。而一本专业技术领域的译作得以成功问世,其中之波折也是再所难免。幸得多位业内专家不吝指导,使得我们的工作备感鼓舞。在本书翻译之初,Diomidis Spinellis教授即给我们提出了许多宝贵的建议,他的指导给予了我们极大的帮助。此外,中国计算机学会数据库专业副主任、西北工业大学博士生导师、计算机学院副院长李战怀教授审阅了译稿,并欣然为本书作序推荐,感激之情,溢于言表。  本书得以顺利付梓,我们还要感谢中国航空工业集团第一飞机设计研究院工程师张贵、西安市公安局网监支队秦磊、西安交通大学电信学院研究生杨骥。三人在各自的技术方向上拥有丰富的实践经验,其中,张贵曾参与开发多个大型飞行模拟器项目,而杨骥和秦磊的研究方向则分别侧重于嵌入式开发和网络安全方面。以上诸位在百忙之中积极参与了本书的翻译工作,对于他们严肃认真的工作态度,笔者表示由衷敬佩。  最高品质的图书始终是作译者永恒的追求。但有一千个读者,就有一千个哈姆雷特。因此,我们也真诚地希望本书的读者能够把阅读中的所想所得与我们分享,也欢迎就书中可能存在的纰漏与我们沟通交流,从而使本书日臻完善,以利来者。  左飞  2011年秋

内容概要

Jolt大奖素有“软件业之奥斯卡”的美称,本丛书精选自Jolt历届获奖图书,以植根于开发实践中的独到工程思想与杰出方法论为主要甄选方向。作者使用了超过600个现实的例子来向你展现如何甄别代码的好坏;如何阅读,应当注意什么,以及如何使用这些知识来改进自己的代码。本书在一些现实中的大型实例基础上,论述了代码阅读的策略,并向读者展示了如何将这些代码阅读和代码理解的技艺运用于实践。

作者简介

自1985年开始,本书作者Diomidis
Spinellis在开发大量开创性的,并受到极高评价的商业和开源项目的过程中,一直在钻研、发展本书中所提及的各项技术,期间他编写和维护的代码行数超过25万行。他在英国伦敦帝国理工学院获得了软件工程方向的硕士学位及计算机科学博士学位。目前,他是希腊雅典经济与商业大学管理科学与技术系的教授。他曾撰写过多部畅销世界的计算机技术图书,包括《架构之美》、《代码质量》和《代码阅读》等。

书籍目录

第1章 导论1
1.1 为何以及如何阅读代码2
1.1.1 将代码作为文献2
1.1.2 将代码作为范例5
1.1.3 维护6
1.1.4 演进6
1.1.5 重用8
1.1.6 检查8
1.2 如何阅读本书9
1.2.1 排版约定9
1.2.2 图表11
1.2.3 练习12
1.2.4 辅助材料13
1.2.5 工具13
1.2.6 提纲13
1.2.7 关于“伟大语言”的争论14
进阶阅读15
第2章 基本编程元素17
2.1 一个完整的程序17
2.2 函数和全局变量22
2.3 while循环、条件和块26
2.4 switch语句29
2.5 for循环31
2.6 break和continue语句34
2.7 字符和布尔表达式36
2.8 goto语句39
2.9 局部重构41
2.10 do循环和整数表达式46
2.11 再论控制结构49
进阶阅读55
第3章 高级C数据类型57
3.1 指针57
3.1.1 链式数据结构58
3.1.2 数据结构的动态分配58
3.1.3 引用调用59
3.1.4 数据元素的访问60
3.1.5 数组类型的参数和返回结果61
3.1.6 函数指针63
3.1.7 用做别名的指针65
3.1.8 指针和字符串67
3.1.9 直接内存访问69
3.2 结构体70
3.2.1 组合数据元素70
3.2.2 从函数中返回多个数据元素71
3.2.3 映射数据的组织方式71
3.2.4 以面向对象方式编程73
3.3 联合体75
3.3.1 有效利用内存空间75
3.3.2 实现多态76
3.3.3 不同内部表征的访问77
3.4 动态内存分配79
3.4.1 空闲内存管理81
3.4.2 包含动态分配数组的结构体83
3.5 typedef声明85
进阶阅读87
第4章 C数据结构89
4.1 向量90
4.2 矩阵和表94
4.3 栈98
4.4 队列100
4.5 映射103
4.5.1 散列表106
4.6 集合108
4.7 链表109
4.8 树117
4.9 图122
4.9.1 节点存储122
4.9.2 边的表示124
4.9.3边的存储127
4.9.4 图的属性129
4.9.5 隐含结构129
4.9.6 其他表示方法130
进阶阅读130
第5章 高级控制流程131
5.1 递归131
5.2 异常机制137
5.3 并行性141
5.3.1 硬件和软件的并行性142
5.3.2 控制模型143
5.3.3 线程实现148
5.4 信号151
5.5 非局部跳转154
5.6 宏替换157
进阶阅读162
第6章 应对大型项目163
6.1 设计和实现技术163
6.2 项目的组织165
6.3 编译过程与makefile文件172
6.4 配置179
6.5 版本控制184
6.6 项目专用工具191
6.7 测试196
进阶阅读203
第7章 编码规范和约定205
7.1 文件的名称和组织206
7.2 缩进208
7.3 格式编排210
7.4 命名约定213
7.5 编程实践217
7.6 过程规范219
进阶阅读220
第8章 文档221
8.1 文档类型221
8.2 阅读文档222
8.3 文档中存在的问题234
8.4 其他文档来源236
8.5 常见的开源文档格式239
进阶阅读245
第9章 架构414
9.1 系统结构248
9.1.1 集中式存储库和分布式方法248
9.1.2 数据流架构252
9.1.3 面向对象结构254
9.1.4 分层架构257
9.1.5 层次260
9.1.6 切片261
9.2 控制模型263
9.2.1 事件驱动系统263
9.2.2 系统管理器266
9.2.3 状态转移268
9.3 元素包装270
9.3.1 模块270
9.3.2 命名空间272
9.3.3 对象276
9.3.4 泛型实现287
9.3.5 抽象数据类型292
9.3.6 库292
9.3.7 进程和过滤器296
9.3.8 组件297
9.3.9 数据存储库299
9.4 架构重用301
9.4.1 框架301
9.4.2 代码向导302
9.4.3 设计模式303
9.4.4 领域专有的架构305
进阶阅读308
第10章 代码阅读工具311
10.1 正则表达式312
10.2 用编辑器浏览代码314
10.3 用grep搜索代码317
10.4 找出文件的差异325
10.5 开发自用工具326
10.6 借助编译器阅读代码329
10.7 代码浏览器与美化器333
10.8 运行时工具338
10.9 非软件工具342
可用工具与进阶读物343
第11章 完整示例345
11.1 概况345
11.2 攻克计划347
11.3 代码重用348
11.4 测试与调试354
11.5 文档361
11.6 观察报告362
附录A 源代码致谢人员名单363
附录B 源代码致谢人员名单363
附录C 源代码致谢人员名单363

章节摘录

版权页:   插图:   在分析一个重要的程序时,最好先找出其最重要的部分。在本例中,这些部分是全局变量(图2—2:1)和函数main(图2—3)、getstops(见图2—5:1)以及usage(见图2—5:8)。 整型变量nstops和整型数组tabstops声明为全局变量,它们的作用域在函数体之外。因此,它们对于所分析文件中的所有函数都是可见的。 紧接着的三个函数声明声明了该文件内之后将出现的函数。由于其中的一些函数可能会在其定义之前使用,因此在C/C++程序中,这些声明允许编译器校验传递给函数的参数以及其返回值,并生成相应的正确代码。如果没有给出前置声明,那么C编译器会依据函数第一次被使用的情形来做出关于函数返回值类型和参数的假设;C++编译器将这种情况标记为错误。如果接下来的函数定义与这些假设不相符,那么编译器将给出一条警告或错误信息。然而,若定义于另一个文件中的函数匹配这个错误的声明,则该通过,程序或许能够编译,但是在运行时会失败。 值得注意的是,两个函数被声明为static,而变量没有。这就是说这两个函数仅仅在该文件中可见,而那些变量则有可能对组成该程序的所有文件都可见。因为expand仅由一个文件组成,所以这一差别在本例中并不重要。多数连接器(用来合并编译后的C文件)都十分原始,对所有程序文件都可见的那些变量(即没有被声明为static的变量),可能会与定义于其他文件中的同名变量进行让人吃惊的交互。因此,在审查代码时,最好确保所有只用于单个文件的变量都声明为static。 下面一起来看一下组成expand的函数。想要了解一个函数(或方法)在做什么,可以采用如下策略之一。 根据函数名猜测。 阅读函数开头的注释。 分析该函数是如何被使用的。 阅读函数体内的代码。 查询外部程序文档。 在本例中,可以很肯定地猜出函数usage将展示有关程序用法的信息然后退出;许多命令行程序都有与之具有同样功能和名称的函数。当你分析大量的代码时,你将会逐渐得出变量和函数的名称以及命名规范。这些将会帮助你正确的猜出它们的用途。然而,你应当做好准备,根据代码阅读过程中必然会出现的一些新证据来随时对最初的猜测做出修改。另外,基于猜测修改代码时,应当设计一个流程来验证最初的假设。这一流程可以包括用编译器检查、引入断言或执行恰当的测试用例。 getsopts的角色比较难于理解。这里没有任何注释,函数体中的代码比较复杂,其名称也可以多种方式解读。注意它被用在程序中一个单独的部分(图2—3:3),这可以帮助我们做进一步的分析。使用到getopt的部分在程序中负责处理程序选项(图2—3:2)。因此,可以肯定地(在本例中也是正确地)认为getopt将处理制表位说明选项。在阅读代码时,这种渐进式的理解较为常见;理解了某一部分的代码可以使得其他部分变得容易理解。基于这种渐进式的理解方式,在理解较为困难的代码时,可以采用类似组合拼图游戏时的策略:从容易的部分开始。

编辑推荐

《Jolt大奖精选丛书:代码阅读》荣获2003年Jolt世界图书大奖,参阅《代码阅读》对于大专院校相关专业的师生、计算机领域的从业人员或稗序设计爱好者都大有裨益。

图书封面

图书标签Tags

评论、评分、阅读与下载


    代码阅读 PDF格式下载


用户评论 (总计34条)

 
 

  •   虽然书中很多内容是跟C系统语言相关的东西,但是整体来说,确实在代码阅读经验上给出了作者自己的经验,很好
  •   学会读一些代码~对于以后coding具有很重要的意义~这本书就告诉了我coding好的代码
  •   书的内容没得说,送货速度也很快,上午订的下午就到了,只是书上的土太多了,不太舒服。此书中包括一张光盘,有很多的开源项目的源代码,可以通过读书以及代码学到不少东西,看看大牛们都是如何写程序的。
  •   好书,还没看,认真阅读中。
  •   比原版的有改进,更加与时俱进。
  •   这本书不错,对一线的人员很有用,对教师也很有启发性。
  •   据同事说还是不错,我是没看懂
  •   还没仔细去看,不过感觉还不错。
  •   重在实践哈
  •   书里的例子大多是C语言的,提到的编码规范,架构重用,测试调试都很有参考价值。
  •   还没读,大至看了一下,还不错。
  •   书像旧书,快递送过来水淋淋的,都没有阅读的兴趣了
  •   就这本书来说,感觉对于没有太多编程经验的人来说,是没啥用的。
  •   书的第一页是坏的 光盘是坏的 打开包裹的时候有点郁闷不过书的内涵还是挺不错的,是大师级人物写的书
  •   对阅读和接手现项目很有用,还对于迅速定位Bug莫大的帮助
  •   这是一本和Kent Beck理念相反的书,指导人们如何阅读作为机器语言的代码而非把代码像写故事一样让人容易阅读。读了一些,我个人感觉不知所云,可能因为写代码的总量太少,体会不到作者的专业精神
  •   一整套的,应该很好
  •   好书,初看了前面几页还行
  •   教你怎么阅读代码
  •   到货快,购书方便
  •   书籍很好都是正版的。
  •   复习了一下 C 的知识,还行
  •     代码阅读有自身的一套技能,重要的是能够确定什么时候使用哪项技术。本书中,作者使用600多个现实的例子,向读者展示如何区分好的(和坏的)代码,如何阅读,应该注意什么,以及如何使用这些知识改进自己的代码。养成阅读高品质代码的习惯,可以提高编写代码的能力。    阅读代码是程序员的基本技能,同时也是软件开发、维护、演进、审查和重用过程中不可或缺的组成部分。本书首次将阅读代码作为一项独立课题,系统性地加以论述。本书引用的代码均取材于开放源码项目——所有程序员都应该珍视的宝库。本书围绕代码阅读,详细论述了相关的知识与技能。“他山之石、可以攻玉”,通过仔细阅读并学习本书,可以快速地提高读者代码阅读的技能与技巧,进而从现有的优秀代码、算法、构架、设计中汲取营养,提高自身的开发与设计能力。    本书适用于对程序设计的基本知识有一定了解,并想进一步提高自身开发能力的读者。
  •      代码阅读方法与实践 Code Reading: The Open Source Perspective http://www.spinellis.gr/codereading/
      
      
      第零章 综述
      提高代码阅读的技能与技巧,进而从现有的优秀代码、算法、架构、设计中汲取营养,提高自身的开发与设计能力。
      恐怕没有哪个伟大的小说家从未读过其他人的著作,没有哪个伟大的画家从未研究过他人的绘画作品,没有哪个技术熟练的外科医生从未观摩过同事如何动手术,没有哪个播音767的机长不是首先在副驾驶员的位置上观看如何实际操作的。
      可是,现实却期望程序员能够做到这些(即,不用读他人的代码就能够编写出优秀的代码。)
      《Unix操作系统注释》、Apache Web服务器、Perl语言、GNU/Linux操作系统、BIND域名服务器和sendmail邮件传输代理程序
      与代码相关的大部分概念:包括变成构造、数据类型、数据结果、控制流程、项目组织、代码规范、文档和构架
      另外一本是介绍:接口和面向应用的代码包括国际化和可移植性问题,常用库和操作系统中的元件、低级嗲吗、领域专有的声明性语言、脚步语言和混合语言系统。
      
       第一章 导论
      1.1 为什么以及如何阅读代码
      1.1.1 将代码作为文献
      1. 要养成一个习惯,经常花时间阅读别人编写的高品质代码。 (读好书、多读书) 就像阅读高品质的散文能够丰富词汇、激发想象力、扩展思维一样,分析设计良好的软件系统的内部结构可以学到新的架构模式、数据结构、编码方法、算法、风格和文档规范、应用程序编程接口(API),甚至新的计算机语言。阅读高品质的代码还可以提高您编写代码的水准。
      
      2. 要有选择地阅读代码,同时,还要有自己的目标。您是想学习新的模式、编码风格、还是满足某些需求的方法?或者,您也许只是在浏览代码,获得其中的某些亮点。在这种情况下,要随时准备仔细地研究那些有趣但尚不了解的部分:语言特性(即使您对一种语言了解得很透切,也不可能尽知它的所有特性,因为现代语言的发展离不开新特性,新的特性总是不断涌现)、API、算法、数据结构、架构和设计模式(design pattern) (搞清楚为什么要阅读代码? 学习架构、学习业务、学习模式、学习编码风格、学习类库... ... 制定目标,对自己要有要求)
      
      3. 要注意并重视代码中特殊的非功能性需求,这些需求也许会导致特定的实现风格。对可移植性、时间或空间效率、易读性、甚至迷惑性的需求都可能导致代码具有非常特殊的特征(如 可维护性、可伸缩性、性能、)
      
      一种积极地阅读代码的方式主动的修改代码来检验您对代码的理解是否正确。再次强调,要从小的改动做起,逐渐地增大他们的范围。通过积极地介入现实的代码,您能够快速地从中了解到新环境的一些基本情况。当您认为已经掌握了他们之后,要考虑投入一些努力(可能要需要投入一些资金)、采取更优组织的方式来学习该环境。阅读相关的书籍、文档或手册,或者参加培训课程;这两种学习方式互相补充。
      另一种积极地阅读现有代码(作为文献)的方式是改进它。如果代码对您或您的团体有价值,请考虑如何来改进它。这可能涉及到使用更好的设计或算法、为某些代码编制文档,或增加功能。开放源代码项目中的代码常常没有很好地编制文档;请将您对代码的理解应用到改进文档上。
      4. 在现有的代码上工作时,请与作者或维护人员进行必须的协调,以避免重复劳动或因此而产生厌恶情绪。
      
      5. 如果您的更难该更为健壮,则考虑申请成为一个版本控制系统的提交者----拥有直接向项目的源代码库中提交代码的授权。请将从开放源码软件中得到的益处看作是一项贷款,尽可能地寻找各种方式来回报开放源码社团。(比如、报告bug,提出问题,解决bug,添加功能贡献代码及文档... ... 人人为我,我为人人)
      
      1.1.2 以代码为范例
      6. 想要了解一个特定的功能是如何实现的。对于某些类型的应用,您也许能够在普通的书籍或专业的出版物和研究论文中得出问题的答案,然而,多数情况下,如果您想要了解“别人会如何完成这个功能呢?”,除了阅读代码以外,没有更好的方法。
      
      1.1.3 维护
      7. 在寻找BUG时,请从问题的表现形式到问题的根源来分析代码。不要沿着不相关的路径(误入岐途) (先搞清楚问题,然后找与问题相关部分代码阅读,记住带有目的性的阅读)
      
      8. 寻找bug,这种情况下,关键的思想是使用工具,我们要充分利用调试器,编译器给出的警告或输出的符号代码,系统调用跟踪器,数据库结构化查询语言(SQL)的日志机制、包转储工具和Windows的消息侦查程序,定出BUG的位置。(要充分利用线索。 侦探破案、医生诊断 都是利用线索或现象的)
      
      1.1.4 演进
      9. 对于那些大型且组织良好的系统,您只需要最低限度地了解它的全部功能,就能够对它做出修改。(一个系统可能有几十个模块,只需要了解与问题相关的模块,就有可能把问题解决,但首先必须模块设计合理)
      10. 当向系统中增加新功能时,首先的任务就是找到实现类似特性的代码,将它作为待实现功能的模板。(做一个新功能首先考虑到的是有没有类似的 模板或例子,有类似模板或例子可以提供借鉴,让你如何实现该功能有更加深入的认识)
      11. 从特性的功能描述到代码的实现,可以按照字符串消息,或使用关键词来搜索代码。
      12. 在移植代码或修改接口时, 您可以通过编译器直接定位出问题涉及的范围,从而减少代码阅读的工作量。
      13. 进行重构时,您从一个能够正常工作的系统开始做起,希望确保结束时系统能够正常工作。一套恰当的测试用例可以帮助您满足此项约束。(测试用例很关键)
      14. 阅读代码寻找重构机会时,先从系统的构架开始,然后逐步细化,能够获得最大的效益。(由宏观到微观,由上到下一种方法)
      1.1.5 重用
      15. 代码的可重用性是一个诱人的,但难以掌握的思想;降低期望就不会感到失望。软件部件一般要经过逐渐地扩展,并重复改写以适用于两个或三个不同系统之后,才会成为可重用的部件;
      16. 如果您希望重要的代码十分棘手,难以理解与分离,可以试着寻找粒度更大一些的包,甚至其他代码。
      1.1.6 审查
      17. 在复查软件系统时,要注意,系统是由很多部分组成的,不仅仅只是执行语句。还要注意分析以下内容:文件和目录结构、生成和配置过程、用户界面和系统的文档。
      18. 可以将软件复查作为一个学习、讲授、援之以手和接受帮助的机会。
      
      1.2 如何阅读本书
      1.2.1 印刷约定
      1.2.2 图解UML
      1.2.3 练习
      1.2.4 辅助材料
       《代码阅读方法与实践》(Code Reading: The Open Source Perspective)中文版[更新CD-ROM] http://www.chnp2p.com/viewthread.php?tid=559279
       随书光盘下载:http://www.ugcn.cn/bencandy.php?fid=231&id=168263
       http://www.spinellis.gr/codequality/
      1.2.5 工具
       Linux下工具:grep find
      1.2.6 大纲
      1.2.7 “重要语言”的争论
       开放源码下载地址: http://sourceforge.net/directory/
      1.2.8 进阶读物
      
      
       第二章 文档(原书:第八章:文档)P196
      
      150.阅读代码时, 应该尽可能地利用任何能够得到的文档.
      
      151.阅读一小时代码所得到的信息只不过相当于阅读一分钟文档.
      
      
      8.1 文档的类型
      152.使用系统的规格说明文档(system specification document), 详细描述系统的目标、系统的功能需求、管理和技术上的限制、以及成本和日程等要素,了解所阅读代码的运行环境.
      153.软件需求规格说明(software requirements specification)提供对用户需求和系统总体结构的高层描述,并且详细记述系统的功能和非功能性需求,比如数据处理、外部接口、数据库的逻辑模式以及设计上的各种约束。由于软硬件环境和用户需求的不断改变,文档中还可能描述预期的系统演化。是阅读和评估代码的基准.
      154.系统的设计规格说明(system design specification)一般描述系统的构架、数据和代码结构,还有不同模块之间的接口。面向对象的设计会勾画出系统的基本类型以及公开方法。细化的设计规格一般还包括每个模块(或类)的具体信息,比如它执行的处理任务、提供的接口,以及与其他模块或类之间的关系。另外,设计规格说明还会描述系统采用的数据结构,适用的数据库模式等。系统的设计规格说明作为认知代码结构的路线图, 阅读具体代码的指引.
      155.系统测试规格说明(system test specification)包括测试计划、具体的测试过程、以及实际的测试结果。每个测试过程都会详细说明它所针对的模块以及测试用例使用的数据(从中可以得知特定的输入由哪个模块处理)。利用测试规格说明文档为我们提供可以用来预演正在阅读的代码.
      用户文档(user documentation),这些文档包括功能描、安装说明、介绍性的引导、参考手册和管理员手册。
      156.在接触一个未知系统时, 功能性的描述和用户指南可以提供重要的背景信息,从而更好地理解阅读的代码所处的上下文.
      
      157.从用户参考手册中, 我们可以快速地获取, 应用程序在外观与逻辑上的背景知识, 从管理员手册中可以得知代码的接口|文件格式和错误消息的详细信息.
      
      
      8.2 阅读文档
      158.利用文档可以快捷地获取系统的概况, 了解提供特定特性的代码.
      159.文档经常能够反映和提示出系统的底层结构.
      160.文档有助于理解复杂的算法和数据结构.
      161.算法的文字描述能够使不透明(晦涩, 难以理解)的代码变得可以理解.
      162.文档常常能够阐明源代码中标识符的含义.
      163.文档能够提供非功能性需求背后的理论基础.
      164.文档还会说明内部编程接口.
      165.由于文档很少像实际的程序代码那样进行测试, 并受人关注, 所以它常常可能存在错误|不完整或过时.
      166.文档也提供测试用例, 以及实际应用的例子.
      167.文档常常还会包括已知的实现问题或bug.
      168.环境中已知的缺点一般都会记录在源代码中.
      169.文档的变更能够标出那些故障点.一般命名为ChangeLog
      170.对同一段源代码重复或互相冲突的更改, 常常表示存在根本性的设计缺陷, 从而使得维护人员需要用一系列的修补程序来修复.
      171.相似的修复应用到源代码的不同部分, 常常表示一种易犯的错误或疏忽, 它们同样可能会在其他地方存在.
      
      8.3 文档存在的问题
      172.文档常常会提供不恰当的信息, 误导我们对源代码的理解. 两种不同类型的歪曲是未记录的特性(undocumented feature)和理想化表述(idealized presentation)
      173.要警惕那些未归档的特性: 将每个实例归类为合理|疏忽或有害, 相应地决定是否应该修复代码或文档.
      174.有时, 文档在描述系统时, 并非按照已完成的实现, 而是系统应该的样子或将来的实现即理想化表述(idealized presentation).
      175.在源代码文档中, 单词gork的意思一般是指”理解”.
      176.如果未知的或特殊用法的单词阻碍了对代码的理解, 可以试着在文档的术语表(如果存在的话)|New Hacker’s Dictionary[Ray96]|或在Web搜索引擎中查找它们.
      
      8.4 其他文档来源
      177.总是要以批判的态度来看待文档, 注意非传统的来源, 比如注释|标准|出版物|测试用例|邮件列表|新闻组|修订日志|问题跟踪数据库|营销材料|源代码本身.
      178.总是要以批判的态度来看待文档; 由于文档永远不会执行, 对文档的测试和正式复查也很少达到对代码的同样水平, 所以文档常常会误导读者, 或者完全错误.
      179.对于那些有缺陷的代码, 我们可以从中推断出它的真实意图.
      
      8.5 常见的开发源代码文档格式
      文档两种类型:
       1 二进制文件:他们的生成和阅读都要使用专利产品,如:Microsoft Word或Adobe FrameMaker;
       2 文本文件:其中包含标记语言(markup language)形式的结构和格式化命令。
       2.1 troff 2.2 Texinfo 2.3 DocBook 2.4 javadoc 2.5 Doxygen 2.6 Unix手册页 2.7 javadoc注释 2.8 Texinfo文档 2.9 DocBook
      
      180.在阅读大型系统的文档时, 首先要熟悉文档的总体结构和约定.
      
      181.在对付体积庞大的文档时, 可以使用工具, 或将文本输出到高品质输出设备上, 比如激光打印机, 来提高阅读的效率.
      
      
      8.6 进阶读物
      
       第三章 系统构架(原书:第九章: 系统构架)、设计模式、编码惯例
      
      9.1 系统的结构
       9.1.1 集中式存储库(centralized repository)和分布式方案(distributed Scheme)
      182.一个系统可以(在重大的系统中也确实如此)同时展示出多种不同的构架类型. 以不同的方式检查同一系统、分析系统的不同部分、或使用不同级别的分解, 都有可能发现不同的构架类型.
      183.协同式的应用程序, 或者需要协同访问共享信息或资源的半自治进程, 一般会采用集中式储存库构架.
      184.黑板系统使用集中式的储存库, 存储非结构化的键/值对, 作为大量不同代码元件之间的通信集线器.
      
      9.1.2 数据流构架(data-flow)
      185.当处理过程可以建模|设计和实现成一系列的数据变换时, 常常会使用数据流(或管道—过滤器)构架.
      186.在批量进行自动数据处理的环境中, 经常会采用数据流构架, 在对数据工具提供大量支持的平台上尤其如此.
      187.数据流构架的一个明显征兆是: 程序中使用临时文件或流水线(pipeline)在不同进程间进行通信.
      
      9.1.3 面向对象的结构(object-oriented)
      188.使用图示来建模面向对象构架中类的关系.
      189.可以将源代码输入到建模工具中, 逆向推导出系统的构架.
      
      9.1.4 分层构架(layered)
      190.拥有大量同级子系统的系统, 常常按照分层构架进行组织.
      191.分层构架一般通过堆叠拥有标准化接口的软件组件来实现.
      192.系统中每个层可以将下面的层看作抽象实体, 并且(只要该层满足它的需求说明)不关心上面的层如何使用它.
      193.层的接口既可以是支持特定概念的互补函数族, 也可以是一系列支持同一抽象接口不同底层实现的可互换函数.
      194.用C语言实现的系统, 常常用函数指针的数组, 表达层接口的多路复用操作.
      195.用面向对象的语言实现的系统, 使用虚方法调用直接表达对层接口的多路复用操作.
       9.1.5 层次(hierarchical decompositon)
      
      196.系统可以使用不同的、独特的层次分解模型跨各种坐标轴进行组织.
       系统的层次结构:
       源代码在目录中的安排
       静态或动态过程调用图
       namespace(C++),package(Ada,Java,Perl),或标识符名称
       类和接口的继承关系
       结构化黑板项(blackboard entry)的命名
       内部类和嵌套过程
       异构数据结构和对象关联中的导航
      
      9.1.6 切片(slicing)
      197.使用程序切片技术, 可以将程序中的数据和控制之间依赖关系集中到一起.切片可以对量模块内聚性。
      
      9.2 控制模型
       系统的控制模型描述构成系统的各个子系统之间如何进行交互。
       9.2.1 事件驱动的系统(event-driven architecture)
       9.2.2 系统管理器(system manager control model)
      198.在并发系统中, 一个单独的系统组件起到集中式管理器的作用, 负责启动、停止和协调其他系统进程和任务的执行.
      199.许多现实的系统都会博采众家之长. 当处理此类系统时, 不要徒劳地寻找无所不包的构架图; 应该将不同构架风格作为独立但相关的实体来进行定位|识别并了解.
      
      9.2.3 状态变迁(state transition model)
      状态变迁模型(state transition model)通过操作数据----系统的状态,来管理系统的控制流程。状态数据决定执行控制权应该走向何处,对状态数据的更改可以重定向
      执行的目标。采用状态变迁控制模型的系统(更常见的是子系统)一般以状态机(state machine)的形式进行建模和构造。
      状态机(state machine)有一组有穷的状态和从一种状态变到另一种状态时执行处理和变迁的规则来定义。两种特殊情况,初始状态(initial state)和最终状态(final state),
      分别规定状态机的起始点和结束条件。 状态机的实现一般是一个switch语句的循环,switch语句的分支对应状态机的状态;每个case语句执行特定状态的处理工作、
      更改状态、并将控制权返回到状态机的顶端。
      状态机一般用来实现简单的反应系统(simple reactive system)(用户界面、恒温器和电动机控制、以及处理自动化应用程序)、虚拟机、解释器、正则表达式模式匹配器和词法分析器。
      200.状态变迁图常常有助于理清状态机的动作.
      
      9.3 元素封装
       9.3.1 模块
      201.在处理大量的代码时, 了解将代码分解成单独单元的机制极为重要.
      202.大多数情况下, 模块的物理边界是单个文件、组织到一个目录中的多个文件或拥有统一前缀的文件的集合.
      9.3.2 命名空间
       模块的一个重要思想就是信息隐藏(information hiding)的原则,它规定与模块相关的所有信息都应该为私有,除非它被特别声明为公开。
      9.3.3 对象
      203.C中的模块, 由提供模块公开接口的头文件和提供对应实现的源文件组成.
      204.对象的构造函数经常用来分配与对象相关的资源, 并初始化对象的状态. 函数一般用来释放对象在生命期中占用的资源.
      205.对象方法经常使用类字段来存储控制所有方法运作的数据(比如查找表或字典)或维护类运作的状态信息(例如, 赋给每个对象一个标识符的计数器).
      206.在设计良好的类中, 所有的字段都应在声明为private, 并用公开的访问方法提供对它们的访问.
      207.在遇到friend声明时, 要停下来分析一下, 看看绕过类封装在设计上的理由.
      208.可以有节制地用运算符增强特定类的可用性, 但用运算符重载, 将类实现为拥有内建算术类型相关的全部功能的类实体, 是不恰当的.
      9.3.4 泛型实现(generic implementation)
       数据结构或算法的泛型实现(generic implementation) 的设计目的是能够在任意数据类型上工作。
      209.泛型实现不是在编译期间通过宏替换或语言所支持的功能(比如C++模板和Ada的泛型包)来实现, 就是在运行期间通过使用数据元素的指针和函数的指针、或对象的多态性实现.
      编写特定概念的泛型实现需要切实的智力劳动。因此,泛型实现最常用在,通过对实现的广泛应用能够获得相当好处的情况下。
      
      典型的应用包括(1)容器(经常作为抽象数据类型来实现) (2)相关的算法,比如查找、排序和其他作用在集合和有序区间上的操作 (3)泛型数值算法
      
      
      9.3.5 抽象数据类型(Abstract data type, ADT)
      
      210.抽象数据类型经常用来封装常用的数据组织方案(比如树|列表或栈), 或者对用户隐藏数据类型的实现细节.
      9.3.6 库
      211.使用库的目的多种多样: 重用源代码或目标代码, 组织模块集合, 组织和优化编译过程, 或是用来实现应用程序各种特性的按需载入.
      9.3.7 进程和过滤器
      212.大型的、分布式的系统经常实现为许多互相协作的进程.
      9.3.8 组件(component)
      9.3.9 数据存储库
      213.对于基于文本的数据储存库, 可以通过浏览存储在其中的数据, 破译出它的结构.
      214.可以通过查询数据字典中的表, 或使用数据库专有的SQL命令, 比如show table, 来分析关系型数据库的模式.
      9.4 构架重用
       框架(Framework)和代码向导(code wizard)是代码级构架重用中的两种机制。
      9.4.1 框架
      215.识别出重用的构架元素后, 可以查找其最初的描述, 了解正确地使用这种构架的方式, 以及可能出现的误用.
      216.要详细分析建立在某种框架之上的应用程序, 行动的最佳路线就是从研究框架自身开始.
      9.4.2 代码向导(code wizard)
      217.在阅读向导生成的代码时, 不要期望太高, 否则您会感到失望.
      9.4.3 设计模式(design pattern)
      217.在阅读向导生成的代码时, 不要期望太高, 否则您会感到失望.
      218.学习几个基本的设计模式之后, 您会发现, 您查看代码构架的方式会发生改变: 您的视野和词汇将会扩展到能够识别和描述许多通用的形式.
      219.频繁使用的一些模式, 但并不显式地指出它们的名称, 这是由于构架性设计的重用经常先于模式的形成.
      220.请试着按照底层模式来理解构架, 即使代码中并没有明确地提及模式.
      9.4.4 领域专有的构架
       编译器(处理某种计算机语言的应用程序):一般都将任务划分成,将字符组织成标记(token的过程)--(lexical analysis,词法分析),以及将标记汇编成更大结构的过程--(parsing,语法分析)。编译器一般遵循,用不同的源代码语义分析进行词法分析和语法分析、优化和代码生成等步骤;大多数解释器将语法分析后的代码转换成内部分析树或字节码表示,之后用状态机或虚拟机对程序进行处理。
       解释器:sed(stream editor,流编辑器)命令解释器围绕一个循环构建,该循环不断读取指令码,并按指令的内容执行相应的动作。分析后的程序在内部存储为一个链表,其中每个元素包含一个指令码和相关的参数。变量cp是解释器状态(program state).
      221.大多数解释器都遵循类似的处理构架, 围绕一个状态机进行构建, 状态机的操作依赖于解释器的当前状态、程序指令和程序状态.
       参考架构(reference archtecture):
      222.多数情况下, 参考构架只是为应用程序域指定一种概念性的结构, 具体的实现并非必须遵照这种结构.
      9.5 进阶读物
      
      第四章 代码阅读工具 (原书:第十章: 代码阅读工具)
      
      223.词汇工具可以高效地在一个大代码文件中或者跨多个文件查找某种模式.
      
      224.使用程序编辑器和正则表达式查找命令, 浏览庞大的源代码文件.
      
      225.以只读方式浏览源代码文件.
      
      226.使用正则表达式 ^function name 可以找出函数的定义.
      
      227.使用正则表达式的字符类, 可以查找名称遵循特定模式的变量.
      
      228.使用正则表达式的否定字符类, 可以避免非积极匹配.
      
      229.使用正则表达式 symbol-1. *symbol-2, 可以查找出现在同一行的符号.
      
      230.使用编辑器的 tags 功能, 可以快速地找出实体的定义.
      
      231.可以用特定的 tag 创建工具, 增加编辑器的浏览功能.
      
      232.使用编辑器的大纲视图, 可以获得源代码结构的鸟瞰图.
      
      233.使用您的编辑器来检测源代码中圆括号|方括号和花括号的匹配.
      
      234.使用 grep 跨多个文件查找代码模式.
      
      235.使用 grep 定位符号的声明|定义和应用.
      
      236.当您不能精确地表述要查找的内容时, 请使用关键单词的词干对程序的源代码进行查找.
      
      237.用 grep 过滤其他工具生成的输出, 分离出您要查找的项.
      
      238.将 grep 的输出输送到其他工具, 使复杂处理任务自动化.
      
      239.通过对 grep 的输出进行流编辑, 重用代码查找的结果.
      
      240.通过选取与噪音模式不匹配的输出行(grep-v), 过滤虚假的 grep 输出.
      
      241.使用 fgrep 在源代码中查找字符串列表.
      
      242.查找注释, 或标识符大小写不敏感的语言编写的代码时, 要使用大小写不敏感的模式匹配(grep -i).
      
      243.使用 grep –n 命令行开关, 可以创建与给定正则表达式匹配的文件和行号的检查表.
      
      244.可以使用 diff 比较文件或程序不同版本之间的差别.
      
      245.在运行 diff 命令时, 可以使用 diff –b, 使文件比较算法忽略结尾的空格, 用 –w 忽略所有空白区域的差异, 用 –i 使文件比较对大小写不敏感.
      
      246.不要对创建自己的代码阅读工具心存畏惧.
      
      247.在构建自己的代码阅读工具时: 要充分利用现代快速原型语言所提供的能力; 从简单开始, 根据需要逐渐改进; 使用利用代码词汇结构的各种试探法; 要允许一些输出噪音或寂静(无关输出或缺失输出); 使用其他工具对输入进行预处理, 或者对输出进行后期处理.
      
      248.要使编译器成为您的: 指定恰当级别的编译器警告, 并小心地评估生成的结果.
      
      249.使用C预处理器理清那些滥用预处理器特性的程序.
      
      250.要彻底地了解编译器如何处理特定的代码块, 需要查看生成的符号(汇编)代码.
      
      251.通过分析相应目标文件中的符号, 可以清晰地了解源文件的输入和输出.
      
      252.使用源代码浏览器浏览大型的代码集合以及对象类型.
      
      253.要抵制住按照您的编码规范对外部代码进行美化的诱惑; 不必要的编排更改会创建不同的代码, 并妨碍工作的组织.
      
      254.优美打印程序和编辑器语法着色可以使得程序的源代码为易读.
      
      255.cdecl 程序可以将难以理解的C和C++类型声明转换成纯英语(反之亦然).
      
      256.实际运行程序, 往往可以更深刻地理解程序的动作.
      
      257.系统调用|事件和数据包跟踪程序可以增进对程序动作的理解.
      
      258.执行剖析器可以找出需要着重优化的代码, 验证输入数据的覆盖性, 以及分析算法的动作.
      
      259.通过检查从未执行的代码行, 可以找出测试覆盖的弱点, 并据此修正测试数据.
      
      260.要探究程序动态动作时的每个细节, 需要在调试器中运作它.
      
      261.将您觉得难以理解的代码打印到纸上.
      
      262.可以绘制图示来描绘代码的动作.
      
      263.可以试着向别人介绍您在阅读的代码, 这样做一般会增进您对代码的理解.
      
      264.理解复杂的算法或巧妙的数据结构, 要选择一个安静的环境, 然后聚精会神地考虑, 不要借助于任何计算机化或自动化的帮助.
      
       第五章 应对大型项目 (第六章: 应对大型项目)
      
      116.我们可以通过浏览项目的源代码树—包含项目源代码的层次目录结构, 来分析一个项目的组织方式. 源码树常常能够反映出项目在构架和软件过程上的结构.
      
      117.应用程序的源代码树经常是该应用程序的部署结构的镜像.
      
      118.不要被庞大的源代码集合吓倒; 它们一般比小型的专门项目组织得更出色.
      
      119.当您首次接触一个大型项目时, 要花一些时间来熟悉项目的目录树结构.
      
      120.项目的源代码远不只是编译后可以获得可执行程序的计算机语言指令; 一个项目的源码树一般还包括规格说明|最终用户和开发人员文档|测试脚本|多媒体资源|编译工具|例子|本地化文件|修订历史|安装过程和许可信息.
      
      121.大型项目的编译过程一般声明性地借助依赖关系来说明. 依赖关系由工具程序, 如make/ant/Maven及其派生程序, 转换成具体的编译行动.
      
      122.大型项目中, 制作文件常常由配置步骤动态地生成; 在分析制作文件之前, 需要先执行项目特定的配置.
      
      123.检查大型编译过程的各个步骤时, 可以使用make程序的-n开关进行预演.
      
      124.修订控制系统提供从储存库中获取源代码最新版本的方式.
      
      125.可以使用相关的命令, 显示可执行文件中的修订标识关键字, 从而将可执行文件与它的源代码匹配起来.
      
      126.使用修订日志中出现的bug跟踪系统内的编号, 可以在bug跟踪系统的数据库中找到有关的问题的说明.
      
      127.可以使用修订控制系统的版本储存库, 找出特定的变更是如何实现的.
      
      128.定制编译工具用在软件开发过程的许多方面, 包括配置|编译过程管理|代码的生成|测试和文档编制.
      
      129.程序的调试输出可以帮助我们理解程序控制流程和数据元素的关键部分.
      
      130.跟踪语句所在的地点一般也是算法运行的重要部分.
      
      131.可以用断言来检验算法运作的步骤|函数接收的参数|程序的控制流程|底层硬件的属性和测试用例的结果.
      
      132.可以使用对算法进行检验的断言来证实您对算法运作的理解, 或将它作为推理的起点.
      
      133.对函数参数和结果的断言经常记录了函数的前置条件和后置条件.
      
      134.我们可以将测试整个函数的断言作为每个给定函数的规格说明.
      
      135.测试用例可以部分地代替函数规格说明.
      
      136.可以使用测试用例的输入数据对源代码序列进行预演.
      
       第六章 一个完整的例子 (第十一章: 一个完整的例子)
      
      265.模仿软件的功能时, 要依照相似实体的线路(类|函数|模块). 在相似的现有实体中, 为简化对源代码库的文本查找, 应选取比较罕见的名称.
      
      266.自动生成的文件常常会在文件的开关有一段注释, 说明这种情况.
      
      267.如果试图精确地分析代码, 一般会陷入数量众多的类|文件和模块中, 这些内容会很快将我们淹没; 因此, 我们必须将需要理解的代码限定在绝对必需的范围之内.
      268.采用一种广度优先查找策略, 从多方攻克代码阅读中存在的问题, 进到找出克服它们的方法为止.
      
       第七章基本编程元素 (第二章:基本编程元素)
      
      19.第一次分析一个程序时, main是一个好的起始点.
      
      20.层叠if-else if-...-else序列可以看作是由互斥选择项组成的选择结构.
      
      21.有时, 要想了解程序在某一方面的功能, 运行它可能比阅读源代码更为恰当.(运行,并debug可以降低理解动态流程的难度)
      
      22.在分析重要的程序时, 最好首先识别出重要的组成部分. (打蛇打三寸,抓住关键部分,可以快速理解)
      
      23.了解局部的命名约定, 利用它们来猜测变量和函数的功能用途.(类名用名词或代词或动名词,最好不要用动词,方法用动词)
      
      24.当基于猜测修改代码时, 您应该设计能够验证最初假设的过程. 这个过程可能包括用编译器进行检查|引入断言|或者执行适当的测试用例.
      
      25.理解了代码的某一部分, 可能帮助你理解余下的代码.
      
      26.解决困难的代码要从容易的部分入手.
      
      27.要养成遇到库元素就去阅读相关文档的习惯; 这将会增强您阅读和编写代码的能力.
      
      28.代码阅读有许多可选择的策略: 自底向上和自顶向下的分析|应用试探法和检查注释和外部文档, 应该依据问题的需要尝试所有这些方法.
      
      29.for (i=0; i<n; i++)形式的循环执行n次; 其他任何形式都要小心.
      
      30.涉及两项不等测试(其中一项包括相等条件)的比较表达式('0' <= c && c <= '9')可以看作是区间成员测试(0 <= c <= 9).
      
      31.我们经常可以将表达式应用在样本数据上, 借以了解它的含义. (用用例去验证表达式)
      
      32.使用De Morgan法则简化复杂的逻辑表达式. (?)
      
      33.在阅读逻辑乘表达式(&&)时, 问题可以认为正在分析的表达式以左的表达式均为true; 在阅读逻辑和表达式(||)时, 类似地, 可以认为正在分析的表达式以左的表达式均为false.
      
      34.重新组织您控制的代码, 使之更为易读.
      
      35.将使用条件运行符? :的表达式理解为if代码.
      
      36.不需要为了效率, 牺牲代码的易读性.
      
      37.高效的算法和特殊的优化确实有可能使得代码更为复杂, 从而更难理解, 但这并不意味着使代码更为紧凑和不易读会提高它的效率.
      
      38.创造性的代码布局可以用来提高代码的易读性.
      
      39.我们可以使用空格|临时变量和括号提高表达式的易读性.
      
      40.在阅读您所控制的代码时, 要养成添加注释的习惯. (在阅读中比较复杂或难懂的地方要加注释备注)
      
      41.我们可以用好的缩进以及对变量名称的明智选择, 提高编写欠佳的程序的易读性.(养成好的编码习惯)
      
      42.用diff程序分析程序的修订历史时, 如果这段历史跨越了整体重新缩排, 常常可以通过指定-w选项, 让diff忽略空白差异, 避免由于更改了进层次而引入的噪音.(SVN/Git是否也有这个功能呢?)
      
      43.do循环的循环体至少执行一次.
      
      44.执行算术运算时, 当b=2n-1时, 可以将a&b理解为a%(b+1). (?)
      
      45.将a<<n理解为a*k, k=2n.
      
      46.将a>>n理解为a/k, k=2n.
      
      47.每次只分析一个控制结构, 将它的内容看作是一个黑盒.
      
      48.将每个控制结构的控制表达式看作是它所包含代码的断言.
      
      49.return, goto, break和continue语句, 还有异常, 都会影响结构化的执行流程. 由于这些语句一般都会终止或重新开始正在进行的循环,因此要单独推理它们的行为.
      
      50.用复杂循环的变式和不变式, 对循环进行推理.
      
      51.使用保持含义不变的变换重新安排代码, 简化代码的推理工作.
      
       第八章 高级C数据类型 (第三章: 高级C数据类型)
      
      52.了解特定语言构造所服务的功能之后, 就能够更好地理解使用它们的代码.
      
      53.识别并归类使用指针的理由.
      
      54.在C程序中, 指针一般用来构造链式数据结构|动态分配的数据结构|实现引用调用|访问和迭代数据元素|传递数组参数|引用函数|作为其他值的别名|代表字符串|以及直接访问系统内存.
      
      55.以引用传递的参数可以用来返回函数的结果, 或者避免参数复制带来的开销.
      
      56.指向数组元素地址的指针, 可以访问位于特定索引位置的元素.
      
      57.指向数组元素的指针和相应的数组索引, 作用在二者上的运算具有相同的语义.
      
      58.使用全局或static局部变量的函数大多数情况都不可重入(reentrant).
      
      59.字符指针不同于字符数组.
      
      60.识别和归类应用结构或共用体的每种理由.
      
      61.C语言中的结构将多个数据元素集合在一起, 使得它们可以作为一个整体来使用, 用来从函数中返回多个数据元素|构造链式数据结构|映射数据在硬件设备|网络链接和存储介质上的组织方式|实现抽象数据类型|以及以面向对象的方式编程.
      
      62.共用体在C程序中主要用于优化存储空间的利用|实现多态|以及访问数据不同的内部表达方式.
      
      63.一个指针, 在初始化为指向N个元素的存储空间之后, 就可以作为N个元素的数组来使用.
      
      64.动态分配的内在块可以电焊工地释放, 或在程序结束时释放, 或由垃圾回收器来完成回收; 在栈上分配的内存块当分配它的函数退出后释放.
      
      65.C程序使用typedef声明促进抽象, 并增强代码的易读性, 从而防范可移植性问题, 并模拟C++和Java的类声明行为.
      
      66.可以将typedef声明理解成变量定义: 变量的名称就是类型的名称; 变量的类型就是与该名称对应的类型.
      
       第九章 C数据结构 (第四章: C数据结构)
      
      67.根据底层的抽象数据类型理解显式的数据结构操作.
      
      68.C语言中, 一般使用内建的数组类型实现向量, 不再对底层实现进行抽象.
      
      69.N个元素的数组可以被序列for (i=0; i<N; i++)完全处理; 所有其他变体都应该引起警惕.
      
      70.表达式sizeof(x)总会得到用memset或memcpy处理数组x(不是指针)所需的正确字节数.
      
      71.区间一般用区间内的第一个元素和区间后的第一个元素来表示.
      
      72.不对称区间中元素的数目等于高位边界与低位边界的差.
      
      73.当不对称区间的高位边界等于低位边界时, 区间为空.
      
      74.不对称区间中的低位边界代表区间的第一个元素; 高位边界代表区间外的第一个元素.
      
      75.结构的数组常常表示由记录和字段组成的表.
      
      76.指向结构的指针常常表示访问底层记录和字段的游标.
      
      77.动态分配的矩阵一般存储为指向数组列的指针或指向元素指针的指针; 这两种类型都可以按照二维数组进行访问.
      
      78.以数组形式存储的动态分配矩阵, 用自定义访问函数定位它们的元素.
      
      79.抽象数据类型为底层实现元素的使用(或误用)方式提供一种信心的量度.
      
      80.数组用从0开始的顺序整数为键, 组织查找表.
      
      81.数组经常用来对控制结构进行高效编码, 简化程序的逻辑.
      
      82.通过在数组中每个位置存储一个数据元素和一个函数指针(指向处理数据元素的函数), 可以将代码与数据关联起来.
      
      83.数组可以通过存储供程序内的抽象机(abstract machine)或虚拟机(virtual machine)使用的数据或代码, 控制程序的运作.
      
      84.可以将表达式sizeof(x) / sizeof(x[0])理解为数组x中元素的个数.
      
      85.如果结构中含有指向结构自身|名为next的元素, 一般说来, 该结构定义的是单向链表的结点.
      
      86.指向链表结点的持久性(如全局|静态或在堆上分配)指针常常表示链表的头部.
      
      87.包含指向自身的next和prev指针的结构可能是双向链表的结点.
      
      88.理解复杂数据结构的指针操作可以将数据元素画为方框|指针画为箭头.
      
      89.递归数据结构经常用递归算法来处理.
      
      90.重要的数据结构操作算法一般用函数参数或模板参数来参数化.
      
      91.图的结点常常顺序地存储在数组中, 链接到链表中, 或通过图的边链接起来.
      
      92.图中的边一般不是隐式地通过指针, 就是显式地作为独立的结构来表示.
      
      93.图的边经常存储为动态分配的数组或链表, 在这两种情况下, 边都锚定在图的结点上.
      
      94.在无向图中, 表达数据时应该将所有的结点看作是等同的, 类似地, 进行处理任务的代码也不应该基于它们的方向来区分边.
      
      95.在非连通图中, 执行遍历代码应该能够接通孤立的子图.
      
      96.处理包含回路的图时, 遍历代码应该避免在处理图的回路进入循环.
      
      97.复杂的图结构中, 可能隐藏着其他类型的独立结构.
      
       第十章 高级控制流程 (第五章: 高级控制流程)
      
      98.采用递归定义的算法和数据结构经常用递归的函数定义来实现.
      
      99.推理递归函数时, 要从基准落伍测试开始, 并认证每次递归调用如何逐渐接近非递归基准范例代码.
      
      100.简单的语言常常使用一系列遵循该语言语法结构的函数进行语法分析.
      
      101.推理互递归函数时, 要基于底层概念的递归定义.
      
      102.尾递归调用等同于一个回到函数开始处的循环.
      
      103.将throws子句从方法的定义中移除, 然后运行Java编译器对类的源代码进行编译, 就可以容易地找到那些可能隐式地生成异常的方法.
      
      104.在多处理器计算机上运行的代码常常围绕进程或线程进行组织.
      
      105.工作群并行模型用于在多个处理器间分配工作, 或者创建一个任务池, 然后将大量需要处理标准化的工作进行分配.
      
      106.基于线程的管理者/工人并行模型一般将耗时的或阻塞的操作分配给工人子任务, 从而维护中心任务的响应性.
      
      107.基于进程的管理者/工人并行模型一般用来重用现有的程序, 或用定义良好的接口组织和分离粗粒度的系统模块.
      
      108.基于流水线的并行处理中, 每个任务都接收到一些输入, 对它们进行一些处理, 并将生成的输出传递给下一个任务, 进行不同的处理.
      
      109.竞争条件很难捉摸, 相关的代码常常会将竞争条件扩散到多个函数或模块; 因而, 很难隔离由于竞争条件导致的问题.
      
      110.对于出现在信号处理器中的数据结构操作代码和库调用要保持高度警惕.
      
      111.在阅读包含宏的代码时, 要注意, 宏既非函数, 也非语句.
      
      112.do…while(0)块中的宏等同于控制块中的语句.
      
      113.宏可以访问在它的使用点可见的所有局部变量.
      
      114.宏调用可改变参数的值
      
      115.基于宏的标记拼接能够创建新的标记符.
      
       第十一章 编码规范和约定 (第七章: 编码规范和约定)
      
      137.了解了给定代码库所遵循的文件组织方式后, 就能更有效率地浏览它的源代码.
      
      138.阅读代码时, 首先要确保您的编辑器或优美打印程序的tab设置, 与代码遵循的风格规范一致.
      
      139.可以使用代码块的缩进, 快速地掌握代码的总体结构.
      
      140.对编排不一致的代码, 应该立即给予足够的警惕.
      
      141.分析代码时, 对标记为XXX, FIXME和TODO的代码序列要格外注意: 错误可能就潜伏在其中.
      
      142.常量使用大写字母命名, 单词用下划线分隔.
      
      143.在遵循Java编码规范的程序中, 包名(package name)总是从一个顶级的域名开始(例如, org, com), 类名和接口名由大写字母开始, 方法
      
      和变量名由小写字母开始.
      
      144.用户界面控件名称之前的匈牙利记法的前缀类型标记可以帮助我们确定它的作用.
      
      145.不同的编程规范对可移植构造的构成有不同的主张.
      
      146.在审查代码的可移植性, 或以某种给定的编码规范作为指南时, 要注意了解规范对可移植性需求的界定与限制.
      
      147.如果GUI功能都使用相应的编程结构来实现, 则通过代码审查可以轻易地验证给定用户界面的规格说明是否被正确地采用.
      
      148.了解项目编译过程的组织方式与自动化方式之后, 我们就能够快速地阅读与理解对应的编译规则.
      
      149.当检查系统的发布过程时, 常常可以将相应发行格式的需求作为基准.
      
      附录A 代码概况
      C类:
      [] netbsd 原始文件:NetBSD 1.5_ALPHA 当前流行度:流行 http://os.chinaunix.net/a2005/0618/965/000000965265.shtml
      [] XFree86-3.3 原始文件:netBSD 1.5_ALPHA FreeBSD 当前流行度:流行
       SendMail
       named域名服务器
       tcpdump
      [] apache 原始文件:apache_1.3.22 当前流行度:流行
      [] perl 原始文件:perl-5.6.1 当前流行度:流行
      C++类:
      [] vcf Visual Component Framework http://www.codeproject.com/Articles/792/Visual-Component-Framework
      [] ACE 原始文件:ACE-5.2+TAO-1.2.zip 当前流行度:流行 ACE:http://www.cs.wustl.edu/~schmidt/ACE.html
      [] socket socket++-1.11
      [] qtchat
      [] demoGL demoGL
      [] OpenCL OpenCL
      [] purenum purenum
      Java类:
      [] jt4 jakarta-tomcat-4.0
      [] argouml
      [] hsqldb
      [] cocoon
  •     个人觉得这本书是讲授代码阅读的经典之作,很多牛人都推荐阅读的。之前常听网络上的蔡学镛大牛说,程序员提高的两个途径,一个是写代码,一个就是阅读代码了。所以推荐想成为高手的非高手,以及想持续成为高手的朋友都读下这本书
  •     技术类的书籍真是什么样的都有,教你怎么阅读代码,看是没有必要,谁不会阅读代码?打开文本编辑器,从上而下,至左到右,而且好一点的编辑器都支持语法加亮,收缩等,如果觉得不够,还可以在IDE里边阅读。但是读完这本书,应该会知道阅读代码也是很讲究的,不是那么简单的。
  •     
      
      
      
      和我想象中不太一样,随书光盘带了很多开源代码的例子,不过本书不是莱昂氏那样条分缕析,而是指点阅读的方法,铺得很开以致感觉有些琐碎,有些内容在别的书里讲的更深入。
      
      个人最喜欢后面几章和附录。
  •     首先,阅读代码这个领域确实很少有书涉及到,作者试图阐述他阅读代码的一些经验,当然,这很值得我们参考,尤其是阅读开源代码的人,可能会从中学到很多开源项目的规范与设计方法,从而更好地去理解open source
      另外,我觉得这本书最重要的地方在于,一,它论述了阅读代码的重要性,我们程序员常常太醉心于写代码,却忘了开源世界宝贵的源码资源,如同作家通常从其他作品中汲取营养一样,一个好的程序员也必然要从其他优秀的代码中学习编码技巧及经验;二,它试图从阅读代码的角度来阐述如何写一个程序,这个视角很新颖,虽然前面几章对于有经验的程序员来说显得有些冗余
      对于那些希望从书中学到更多阅读代码技巧的人,可能会有些失望.因为看完这本书,你面对一个大系统,可能依然无从下手.
      我给这本书四星,因为这本书很不错,但还不够好
  •     不是C的普及读本,
      
      而是从代码方面进行黑客文化的回忆,
      
      通过代码阅读方法,可以体验到几十年积累下来的文化规约^^^^
      
      
      
  •     亚马逊上这本书的评价是四颗星,所以一开始还有些期待,尽管刚看了目录就觉得它不是很诱人。
      后来发现亚马逊上即使给它打四五颗星的,还是说了它不少不足。
      耐着性子用一个小时翻完这本书,发现亚马逊上指出的不足都非常中肯,比如没有重点、凌乱、只适合初学者等等。
      这本书中有太多莫名其妙的东西,根本就只应该出现在C语言基础教程中。与我的预期相差太远。
      倒是觉得,每一章节末尾的“进阶阅读”列表很不错,它差不多说明了你要阅读开源程序所需要具备的知识与能力,是一个颇详细的reading list。
  •   真失望,我是来找方法的。。
  •   补充:
    又仔细看了进阶阅读列表,发现写书的这个希腊人还真是个老学究式的人物呢。Dijkstra 68年写的关于goto语句的文章都能被他翻出来列上。
    平心而论,这本书一定包含了他几十年的读书笔记,不简单呢。可是就是不好看。
  •   哈哈,只有一笑,无语……
  •   我觉得代码阅读因代码而异
 

250万本中文图书简介、评论、评分,PDF格式免费下载。 第一图书网 手机版

京ICP备13047387号-7