首页资源分类嵌入式系统其他 > 代码阅读-code reading

代码阅读-code reading

已有 451617个资源

下载专区

上传者其他资源

嵌入式系统热门资源

本周本月全部

文档信息举报收藏

标    签:代码代码阅读

分    享:

文档简介

代码阅读讲述代码阅读的方法,可以有效避免弯路

文档预览

Jolt 大奖精选丛书 代码阅读(含光盘 1 张) 权威精选植根于开发实践的最佳读 物 (希) 斯宾耐立思 (Spinellis,D.) 著 左飞, 吴跃, 杨宁译 ISBN 978-7-121-17481-0 2012 年 8 月出版 定价:79.00 元 16 开 416 页 宣传语:一项技术产品只有在获得了 Jolt 奖之后才能真正成为行业的主流,一本技术图书 只有在获得了 Jolt 奖之后才能真正奠定经典的地位。 内容简介 Jolt 大奖素有“软件业之奥斯卡”的美称,本丛书精选自 Jolt 历届获奖图书,以植根于开 发实践中的独到工程思想与杰出方法论为主要甄选方向。作者使用了超过 600 个现实的例子 来向你展现如何甄别代码的好坏;如何阅读,应当注意什么,以及如何使用这些知识来改进 自己的代码。本书在一些现实中的大型实例基础上,论述了代码阅读的策略,并向读者展示 了如何将这些代码阅读和代码理解的技艺运用于实践。 本书荣获 2003 年 Jolt 世界图书大奖,参阅本书对于大专院校相关专业的师生、计算机 领域的从业人员或程序设计爱好者都大有裨益。 出版说明 经久不息的回荡 今时的读书人,不复有无书可读之苦,却时有品种繁多而无从择优之惑,甚而专业度颇 高的技术书领域,亦日趋遭逢乱花迷眼的境地。此时,若得觅权威书评,抑或有公信力的排 行榜,可按图索骥,大大增加选中好书的命中率。然而,如此良助,不可多得,纵观中外也 唯见一枝独秀——素有“软件业奥斯卡”之美誉的 Jolt 奖! 震撼世界者为谁 在计算设备已经成为企业生产和日常生活之必备工具的今天,专业和大众用户对于软件 的功能、性能和用户体验的要求都在不断提高。在这样的背景下,如何能够发挥出软件开发 的最高效率和最大效能,已经是摆在每一个从业者面前的重大课题,而这也正是 Jolt 大奖横 空出世的初衷及坚持数年的宗旨。 Jolt 大奖历时 20 余年,在图书及软件业知名度极高,广受推崇。奖如其名,为引领计 算机科学与工程发展主流,Jolt 坚持将每年的奖项只颁给那些给整个 IT 业界带来震撼结果 的图书、工具、产品及理念等,因一流的眼光及超高的专业度而得以闻名遐迩,声名远播。 除图书外,Jolt 针对软件产品设有诸多奖项分类,如配置管理、协作工具、数据库引擎 /数据库工具、设计工具/建模、开发环境、企业工具、库/框架、移动开发工具等。但图书历 来是 Jolt 大奖中最受瞩目且传播最广的一个奖项分支。Jolt 曾设有通用类图书、技术类图书 等分类,每个分类又设有“卓越奖(” Jolt Award,一般为一个)和“生产力奖(” Productivity Award, 一般为 2 或 3 个)。获奖技术图书一经公布,即打上经典烙印,可谓一举“震撼全世界”(赞 助商 Jolt 可乐的广告词)。 作为计算机技术图书的厚爱者,我们总在追问——是谁在震撼世界,是谁在照亮明天? Jolt 大奖恰似摆在眼前的橱窗,让我们可以近距离观看潮流在舞蹈,倾听震撼在轰鸣! 朝花夕拾为哪般 Jolt 像是一年一度的承诺,在茫茫书海中为我们淘砺出一批批经得起岁月冲刷的杰作, 头顶桂冠的佳作也因而得以一批批引进中国,为国人开阔了眼界,滋补了技术养分。然而, 或因技术差距造就的生不逢时、水土不服,或因翻译、制作的不如人意,抑或是疏于宣传等 诸多原因,这些经典著作在国内出版后,尽管不乏如获至宝的拥趸,却仍不为诸多人所知, 从而与大量本应从中获益的读者擦肩而过。既然这生生错失的遗憾本不该发生,则更不应延 续。为此,我们邀国外出版同行、国内技术专家一道,踏上朝花夕拾之路,竭力为广大读者 筛选出历久弥新、震撼依旧的 Jolt 图书精品。 Jolt 获奖图书皆由业界专家一致评出,并得到软件从业人员的高度认可,虽然这些书今 天读来,不再能看到上世纪史诗时代那般日新月异的理论突破,以及依赖于高深繁复的科学 研究所取得的系统化成果,更多是在日复一日的开发实践中总结和提炼出来的工程思想和方 法论。重新选材之所以有所弃取,从 Jolt 多年来的评奖规律中可窥端倪—— 一万小时真理见 凡是在工程思想领域取得革命性、颠覆性突破的图书,就被归于“震撼”获奖分类。比如, 从基于过程的程序设计模型过渡到面向对象的全新模型,就是软件开发思想上的一次带来巨 大震撼的革命;再比如,打破传统的瀑布模型而转向持续集成的软件交付模型,这也是一场 业界的重大思想转变。像这样的重大思想突破,可以说是数年甚至数十年一遇的,而荣获 Jolt 大奖的图书中更为常见的,则是基于最佳实践的“生产效率”获奖者。获得此类殊荣的图 书,都是作者们从平凡的、重复的,甚至用一般人的眼光看来不怎么起眼的日常开发实践中, 以独具的慧眼、过人的耐心和大胆的创新,闯开一条不平常道路的心血与经验总结。 这些图书所涉及的主题,都是普通的软件开发人员每天要面对的工作——代码阅读、撰 写测试用例、修复软件问题……但就是这样貌似平淡无奇的工作,是否能每一天、每一个项 目都做好,着实拉开了软件开发人员素质的差距,也决定了软件企业开发出来的产品和服务 的质量。我们中国有一句古话,叫做熟能生巧;某位著名企业家也说过一句家喻户晓的名言: “把简单的事千百万次地做好,就是不简单的。”这些朴素而实际的真理,同样也是本套丛书 最能彰显的所谓程序员精神。它建立在脚踏实地的实践基础之上,也充满了对于自由和创新 的向往。 名作可堪比名曲 就不因岁月流逝而褪色来说,与这些 Jolt 名作相媲美者,只有那些百年响彻、震撼古今 的经典名曲。希望本丛书带给大家的每部著作,也如百听不厌的乐曲,掩卷良久方余音绕梁, 真知存心。仔细想来,软件开发与古典音乐岂非有异曲同工之妙?既是人类心智索问精确科 学的探究,亦是寻觅美学享受的追求。工程是艺术的根基,而艺术是工程的极致。衷心地希 望各位读者能够认真阅读本丛书的本本珍品,并切实地用于自己的日常工作中,在充分享受 大师魅力的同时,为中国的软件事业谱写更多、更震撼的乐章。 电子工业出版社博文视点 二○一二年春 原作者中文版序 中国是首个将我的“开源视角”系列作品再版的国家。这可能有很多原因,而其中一个特 别吸引人的原因与孔子的著作有关,他在《论语》中广泛地强调了学习研究的重要性。回顾 之前撰写《代码阅读》和《代码质量》的历程,我了解到,实际上,我鼓励了那些从事开发 工作的同事和学生借助研究学习软件代码来提升他们自身的知识和技能,这正是我遵循孔子 金玉良言的一种方式。 《代码阅读》一书阐述了开发者应当如何阅读已有的代码。关于为何要进行代码阅读, 不少人也给出了许多现实中的原因:修正问题、添加特性、寻找有用的片段,或者作为你所 在机构质量控制流程的一部分对其进行复查。然而,进行代码阅读最重要的原因其实是从中 学习。从已有的高质量代码中,我们可以学习如何将严谨的代码风格应用于实践,如何编写 有用的注释,如何编排代码以方便他人读懂,如何选择有意义的标识符,以及如何将复杂的 代码组织为可管控的部分。另外,通过研究代码,我们还可以学习到新的算法、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 教授即 给我们提出了许多宝贵的建议,他的指导给予了我们极大的帮助。此外,中国计算机学会数 据库专业副主任、西北工业大学博士生导师、计算机学院副院长李战怀教授审阅了译稿,并 欣然为本书作序推荐,感激之情,溢于言表。 本书得以顺利付梓,我们还要感谢中国航空工业集团第一飞机设计研究院工程师张贵、 西安市公安局网监支队秦磊、西安交通大学电信学院研究生杨骥。三人在各自的技术方向上 拥有丰富的实践经验,其中,张贵曾参与开发多个大型飞行模拟器项目,而杨骥和秦磊的研 究方向则分别侧重于嵌入式开发和网络安全方面。以上诸位在百忙之中积极参与了本书的翻 译工作,对于他们严肃认真的工作态度,笔者表示由衷敬佩。 最高品质的图书始终是作译者永恒的追求。但有一千个读者,就有一千个哈姆雷特。因 此,我们也真诚地希望本书的读者能够把阅读中的所想所得与我们分享,也欢迎就书中可能 存在的纰漏与我们沟通交流,从而使本书日臻完善,以利来者。联系邮箱:heiswk@qq.com。 目录 第 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 左飞 2011 年秋 进阶阅读 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 1 第 章 导论 最近我再次查看了我的质因子和井字棋程序,非常遗憾,它们完全没有任何形式的注释 和文档。 ——Donald E. Knuth 软件源码是用以交互程序操作,并将知识通过一种可执行的形式加以存储的明确媒介。你可 以将源代码编译成一个可执行的程序,可以阅读代码弄明白程序要干什么,以及如何工作, 也可以修改代码改变程序的功能。大部分编程课程和教科书都关注于如何从最初的草案开始 编写程序上。然而,软件开发过程中,40%到 70%的工作量是在系统首次编写完成后投入的。 这些工作总是包括阅读、理解和修改原始代码。此外,历史遗留代码总是在不可避免的持续 累积,软件复用也越来越被重视,高的人员流动率总是同软件产业如影随形,开源开发与合 作开发工作(包括外包、代码走查和极限编程)的重要性正日益增强,以上这些都使得代码 阅读成为软件工程师今时今日必不可少的一项重要技能。更进一步讲,通过阅读现实世界中 的优质代码还可以学到如何设计开发关键系统,这种能力是无法通过编写小型程序来学习的。 程序理应为读而写,况且,无论代码是否易读,它们终究需要被人们阅读。用罗伯特•格拉 斯的话来说,尽管代码阅读是“一项少人称颂又缺乏训练的活动”[Gla00],但它本不应该被 这样对待。 本书将通过源自现实中的重要开源系统实例,介绍如何阅读代码。采用一种非常自由的代码 解说方式,应用于项目中所有机器可读的部分:源代码(及其注释)、文档、可执行程序、 源代码库、设计图表和架构草案。通过本书,你将学到以下技能:  能够阅读理解重要软件的代码。  理解很多重要的软件开发思想。  知道如何探索庞大的代码体。  拥有阅读大多数重要的高级(或低级)编程语言的能力。  理解实际软件项目的复杂性。 尽管本书是以回顾基本的编程结构作为开头,但笔者假设你对 C、C++或者 Java 已比较熟悉, 能够使用简单的工具查看在网站上提供的范例代码。此外,对我们所讨论的系统和应用程序 有所了解(尽管这不是必需的)将有助于进一步领会本书材料。 在本章余下的部分,将讨论为何阅读代码,以及相应的阅读策略,同时还提供了一份简明的 “指导手册”来帮助使用本书提供的材料。祝您的代码阅读之旅愉快! 1.1 为何以及如何阅读代码 有时代码阅读是一件不得不做的事,比如在修改、检查和改进已有代码的时候。有时,你也 可能会出于学习的目的来阅读代码,在这种情况下,工程师总是会倾向于查看一下事物的内 部结构,当其盖子被揭开的时候。也可能会为了从材料中去粗取精,提取可供复用的素材, 也可能纯粹是出于个人喜好,就像阅读文学作品一样(尽管这种情况很少,但我们希望在读 完本书之后它将变得更为常见)去阅读代码。对于以上每种原因,代码阅读都有自己的一套 技巧,侧重于不同的技能 1。 1.1.1 将代码作为文献 迪克•加布里埃尔指出,我们所从事的是少数几个不允许作者阅读彼此作品的创造性行业之 一[GG00]。 受所有权观念的影响,已经没有软件可以用做文献。这就好比所有的作家都拥有自己的公司, 只有属于梅尔维尔公司的人能够阅读《白鲸》2,只有海明威公司的人能够阅读《太阳照常 升起》3。能够想象在这样的环境下会产生丰富的文学作品吗?在这样的条件下,既不会有 文学课程,也没有办法教授写作。那么我们希望人们在同样的环境下学习编程吗? 开源软件已经使情况发生了改变:现在我们可以访问数百万行的代码(代码质量有高有低), 可以阅读、评论和改进这些代码,也可以从中有所收获。事实上,作为一个科学的通信传播 手段,社会的进步已经造就了数学定理方面的成功,它们也同样适用于开源软件。大部分开 源软件程序已经被:  以源代码的形式归档、发布和检查。  论证、内化、泛化和释义。  用以解决实际的问题,经常是与其他程序一起。 养成阅读他人编写的高质量代码的习惯。就像阅读优秀的散文能够丰富词汇量,激发灵感, 开阔视野一样,检查剖析一套设计良好的软件系统的内部细节可以学到新颖的架构模式、数 据结构、编码方法、算法、风格和文档规范、应用程序编程接口,甚至是一门新的计算机语 言。阅读高质量的代码还可以提高你编写代码的水准。 在代码阅读之旅中,不可避免地会遇到这样一些代码,它们最好是被当做活生生的反例来规 避。能够快速区分好代码和差代码是一项非常有价值的技能;接触一些编码的反面例子有助 于培养这种技能。通过下面这些特征,可以轻松地识别出较低品质的代码:  编码风格不一致。  结构上毫无道理地复杂或可读性较差。 1 非常感激 Dave Thomas 对于该部分的建议。 2 梅尔维尔(Herman Melville,1819—1891),美国作家、诗人,被誉为美国的“莎士比亚”。《白鲸》 是梅尔维尔于 1851 年发表的一篇关于海洋题材的小说,被认为是美国最伟大的小说之一。——译者注 3 海明威(Ernest Hemingway,1899—1961),美国作家,诺贝尔文学奖获得者。《太阳照常升起》是海 明威的代表作之一。——译者注  明显的逻辑错误或疏忽。  过度使用不可移植的结构。  缺乏维护。 然而,不要指望从糟糕的代码中学到高超的编程技巧;如果将这样的代码当做文献来读,那 是在浪费时间,特别是现在可以访问到那么多的高质量代码。 扪心自问:“我正在阅读的代码真的是最好的吗?”开源运动的优势之一就是,成功的软件 项目和想法会激励竞争者不断地改进软件的结构和功能。我们时常会有幸看到对软件设计的 第二次或者第三次重复;大多数情况下(但不总是如此),后面的设计总是要比之前的版本 有显著的提升。根据正在探寻的功能,使用相应的关键词在 Web 上搜索,可以很容易地找 到各种相互竞争的实现方式。 有选择地阅读代码并在头脑中保有一个目标。你是否在试着学习新的模式、一种编码风格, 或者满足某些需求的方法?或者,也可能只是浏览代码,以获得其中的某些闪光之处。如果 是,准备好仔细研究那些有趣但尚不了解的部分:语言特性(即便你对一种语言了解得很透 彻,也不可能完全知道它的所有特性,因为现代语言的发展离不开新的特性,新的特性总是 不断涌现)、应用程序编程接口、算法、数据结构、架构和设计模式。 注意并甄别代码中特殊的非功能性需求,这些需求也许会引发特定的实现风格。对兼容性、 时间或空间效率、可读性,甚至迷惑性的需求都可能导致代码具有非常特殊的特征。  我们曾经看到过,有些代码为了与过去的链接程序保持兼容,使用了由 6 个字母组 成的外部标识符。  有些高效算法的实现(就源代码的行数而言)比其相应的自然实现要复杂两个数量 级。  嵌入式或空间受限的应用程序(如 GNU/Linux 或 FreeBSD 在软盘上发布的各种发布 版),它们的代码可能会为了节省几个字节的空间而增长很多。  专为演示算法运作原理而编写的代码,其使用的标识符可能会长得离谱。  某些应用领域,如复制保护方案,可能要求代码难以阅读,以防被逆向工程(通常 是徒劳的)。 如果读到了这些类型的代码,要注意这些特定的非功能性需求,并留意同行们是如何满足这 些需求的。 有时,你可能会发现所阅读的代码完全来自于自己并不熟悉的环境(计算机语言、操作系统 或应用程序编程接口)。在熟悉基本的编程和计算机科学概念之后,大多数情况下,可以把 源代码当做是一种掌握新环境下基本情况的途径。但要注意从小型的程序开始阅读;不要马 上一头扎到大型系统的研究中。编译所研究的程序并运行。这样可以得到即时的回馈,既可 以了解代码预想的工作方式,又可以收获一份成就感。下一步就是主动地修改代码,以此来 检验自己对代码的理解。还是要从小的改动做起,然后逐渐扩大范围。通过积极地介入现实 的代码,可以快速地了解新环境下的一些基本情况。在掌握之后,要考虑投入一些努力(可 能还需要投入一些资金),采取更系统的方式来学习该环境。阅读相关的书籍、文档或手册, 或者参加培训课程;这两种学习方式互为补充。 另外一种积极地阅读现有代码的方式是改进它,而这些代码都是被当做文献来阅读的。与其 他文学作品相比,软件代码是活的手工艺品,会持续不断地得到改进。如果代码对你或你的 团体有价值,请考虑如何去改进它。这可能涉及使用更好的设计或算法、为某些代码归档, 或者增加功能。开源项目中的代码常常缺乏较好的配套文档;请将你对代码的理解应用到改 进文档上。在现有的代码上工作时,请与作者和维护人员进行必要的协调,以避免重复劳动 或因此滋生不良情绪。如果你的调整更为健壮,则考虑申请成为一个并发式版本控制系统 (CVS)的提交者——有权向该工程的源码库直接提交代码的个体。请将从开源软件中得到 的益处看做是一项贷款;尽可能回馈开源社团。 1.1.2 将代码作为范例 在某些情况下,你也许会发现自己对某个特定的功能如何实现感到好奇。对于某种应用,也 许能在普通的教科书或专业出版物和研究论文中得到问题的答案。然而,多数情况下,想要 知道“别人是怎么做的”,只有阅读代码。想要创建与给定实现相兼容的软件,代码阅读也 可能是最可靠的方式。 在将代码用做范例时,关键的思想是灵活性。要准备好通过大量不同的策略与方法来了解代 码的工作方式。从所能找到的任何文档作为起点(参见第 8 章)。最理想的情况是能够找到 正式的软件设计文档,不过即使是用户文档也很有帮助。实际使用这一系统,了解它的外部 接口。要清楚自己在找什么:是一个系统调用、一种算法、一段代码,还是一种架构?设计 一种发现目标代码的策略。不同的搜索策略适用于不同的目的。你可能需要跟踪指令执行的 序列,运行程序并在关键位置设置断点,或者对代码进行文本查找,以找出特定的代码或数 据元素。可以借助工具(参见第 10 章),但不要局限于其中任何一种。如果一种策略不能很 快产生所期待的结果,那就放弃它,然后尝试其他的策略。切记,要找的代码就在那里,我 们只需要找到它。 定位到目标代码后,撇开不相关的部分,对它进行研究分析。这是一项必备技能。本书中的 许多练习都是要求读者完成这种任务。如果你觉得在原来的上下文中理解代码有困难,那就 将它复制到一个临时文件中,删除所有不相关的部分。这一过程的正式名称叫做切片(Slicing) (参见 9.1.6 节),本书中,我们已经非正式地将这种方法应用到了那些被注释的代码示例中, 分析我们的用法,你同样可以了解这个思想。 1.1.3 维护 在其他情况下,代码可能不是学习范例,而是需要被修复的。如果自认为发现了一个大型系 统中的 bug,你需要采取一些策略,不断细化对代码的阅读,直到找出问题。这种情况下, 关键的思想是使用工具。要充分利用调试器、编译器给出的告警或符号代码、系统调用跟踪 器、数据库中 SQL 的日志机制、包转储工具和 Windows 中的消息侦查程序,来定位 bug。(请 参阅第 10 章以了解更多关于“工具如何帮助代码阅读”的内容。)从问题的表现形式到问题 的根源来分析代码。不要沿着不相关的路径往下走。编译程序时加入调试支持,并使用调试 器的栈跟踪机制、单步执行,以及数据和代码断点来缩小搜索范围。 如果调试器不能提供支持(调试某些在后台运行的程序有时是极度困难的,例如守护程序和 Windows 的服务、C++中基于模板的代码、小服务程序和多线程代码),可以考虑在程序执 行路径中的关键位置加入输出语句。在分析 Java 代码时,可以使用 AspectJ 向程序中插入只 在特定情况下运行的代码元。如果问题与操作系统接口有关,系统调用跟踪机制常常能够引 导你逼近问题之所在。 1.1.4 演进 在多数情况下(某些测算显示,超过总时间的 80%),阅读代码不是为了修复缺陷,而是为 了增加新的功能、修改现有的特性、调整代码使其适应新的环境和需求,抑或重构以加强代 码的非功能性质量。在这些情况下,关键的思想是对正在检查的代码范围加以选择;在多数 情况下,确实必须理解的代码在系统全部实现中只占很小的一块。在实际工作中,修改一个 百万行级代码的系统(如典型的内核或窗口系统),可以只是选择性地了解与改变一两个文 件;笔者强烈建议大家亲身体会一下由此而来的愉悦。在有选择地处理大型系统的各个组成 部分时,可以采用的策略如下:  定位到感兴趣的代码部分。  单独了解各个特定的部分。  推断摘选出的代码与其余代码的关系。 在向系统中增加新功能时,首要任务是找到实现类似特性的代码,将其作为待实现功能的一 个模板。与之类似,在修改已有的特性时,首先要找到底层的代码。为了从特性的功能描述 开始,走向代码的具体实现,可以跟随字符串消息,或使用关键词来搜索代码。例如,要定 位 ftp 命令中进行用户身份验证的代码,可以在代码中查找字符串“Password”: if (pass == NULL) pass = getpass("Password:"); n = command("PASS %s", pass); 之后,开始研究其实现(跟踪任何你认为相关的代码部分)、设计或增加新的特性,以及定 位可能受影响的区域——代码中与新代码发生交互的其他部分。在大多数情况下,只需彻底 搞懂这些代码足矣。 调整代码使之适应新的环境和上面的任务有所不同,它需要采取另一套策略。有一些情况下, 两种环境提供相似的功用:或许是正将代码从 Sun Solaris 移植到 GNU/Linux,或从 UNIX 系 统移植到 Microsoft Windows。在这些情况下,编译器可以成为你最有价值的朋友。在刚刚 开始时,假定你已经完成任务,并试图编译整个系统。依照编译与链接错误的指示,有条不 紊地修改代码,直到最终能够干净利落地完成整个链接过程,然后验证系统的各项功能。你 会发现,采用这种方法可以极大地减少需要阅读的代码量。在修改了函数、类、模板或数据 结构的接口后,也可以遵从相似的策略。在很多情况下,不用手动地定位更改所造成的影响, 只需按照编译器的错误与警告消息来定位问题点即可。修改这些地方经常会产生新的错误; 通过这个过程,编译器会为你揭开受到新代码影响的代码区域之所在。 如果代码的新环境完全不同于原来的环境(例如,将命令行工具移植到图形窗口环境中), 就必须采用别的方法。在此,最小化代码阅读量的唯一希望是把关注的焦点聚集在老代码和 新环境之间的接口有区别的地方。对于所述例子,这意味着要专注于同用户有交互的代码, 而完全忽略系统中所有与算法有关的方面。 还有一种完全不同的代码演进叫做重构(refactoring)4。随着某些类型的开发工作开始采用 极限编程和敏捷编程方法,这种演进正变得越来越重要。重构行为对系统的更改,是保持系 4 重构是面向对象软件行业所提出的一种改进软件设计方案。在 Martin Fowler 所著的《重构——改善既 有代码的设计》一书中,作者认为过去的软件设计是类似《设计模式》一书中所描述的,在前期优良 设计的前提下构建软件。但是由于软件发展本身不可能是完美的,因此改进的过程是持续且必然的, 重构作为改善软件设计的一种手段和方式,将更加有现实意义。作者同时指出,《设计模式》一书可 作为软件设计的目标,而重构将成为实现目标的手段。——译者注 统的静态外部行为不变,而增强其非功能性品质,例如:简单性、灵活性、易理解性或高效 性。重构和整容手术有一个共同的属性。重构时,从可运转的系统开始,并要确保完成后其 仍可正常运转。一套有针对性的测试用例可以帮助满足这一约束,因此应该从编写测试用例 开始展开重构。有一种重构与修复已知的问题点有关。此时,将必须理解那些老旧的代码部 分(这也是本书所涉及的问题),设计新的实现方式,研究你的代码对相关的其他代码造成 的影响(多数情况下,新代码能够水到渠成地完成替换)并实现更改。 有一种重构是要在软件系统上花费一些“品质时间”,主动寻找可以改进的代码。这是少数 几种需要总览系统整体设计与架构的情形之一。大规模的重构好像要比小规模的重构可以带 来更多的好处。第 6 章讨论了针对大型系统的各种方式,第 9 章则概括了如何从代码转向系 统架构。在阅读代码寻找重构机会时,可以从系统的架构开始,然后逐步深入更深化级别的 细节,从而取得最大的收获。 1.1.5 重用 阅读代码也可能是为了寻找可供重用的部件。在此,关键理念是控制期望值。代码的可重用 性是一个很诱人,但难以掌握的思想;调低期望值就不会感到失望。编写可重用的代码是很 困难的。多年来,很少有软件经得住时间的考验,在多种不同的情况中被重复使用。软件部 件,一般要经过逐渐地扩展,并反复调整至适用于两三个不同的系统后,才会成为可重用的 部件;专为特别目的而开发的软件很少会满足这些条件。事实上,根据 COCOMO Ⅱ软件成 本模型[BCH+95],编写可重用的软件会增加 50%的开发工作量。 在寻找可重用的代码以解决所面对的具体问题时,首先要将可以解决这一问题的代码分离出 来。多数情况下,在系统中基于关键字展开搜索可帮助找到相关的实现。如果打算重用的代 码十分棘手、难以理解,也不易分离,可以试着寻找粒度更大一些的包,甚至其他代码。例 如,不要钻牛角尖去梳理某个代码段与其周围部分之间难于处理的关系,而是使用整个库、 组件、进程,甚至代码所在的系统。 另一种重用的活动是主动检查代码,发掘可重用的部分。在此,最好的办法是寻找已被重用 的代码,也许在所检查的系统中即被重用。显示代码可以重用的积极信号包括一个恰当的包 装方法的运用(参见 9.3 节)或一个配置机制。 1.1.6 检查 最后,在一些工作安排中,代码阅读工作可能是你工作描述的一部分。大量的软件开发方法 学将技术检测作为开发过程中的一条必由之路,如:走查、审查、循环复查,以及作为开发 流程中密不可分的一部分的其他类型的技术评估。此外,在应用极限编程方法的过程中,进 行结对编程时,经常需要在同伴编写代码的同时阅读它们。在这些情形中,代码阅读要求不 同的理解、领会和警惕水平。需要一丝不苟地检查代码,去找到功能和逻辑上的错误。边白 处用危险符号(参见外侧的图标)标记的是一些你需要警惕的东西。另外,应该准备好讨论 那些你没有弄明白的部分;看看代码是否满足所有的需求。 代码的非功能性问题也应该给予同等的重视。代码是否符合组织的开发规范和风格?是否存 在重构的可能?部分代码是否可以编写得更易读或者更加高效?某些部分是否可以重用现 有的库或组件?在复查软件系统时,要注意系统是由很多部分组成的,而非只是可执行的语 句。还要检查以下内容:文件和目录结构,构建和配置过程,用户界面,以及系统文档。 软件检查和相关的活动涉及许多工作人员之间的交互,可以将软件复查作为一个学习、讲授、 帮助他人也接受帮助的机会。 1.2 如何阅读本书 在这本书中,我们示范了重要的代码阅读技巧,并概括了常见的编程理念(我们是以这些理 念在实践中的具体形式来对其进行描述的),以期借此提升读者的代码阅读能力。尽管接下 来的章节中讨论了许多重要的计算机科学和计算实践的思想,例如数据与控制结构、编码标 准和软件架构,然而它们的处理都是比较粗略的,因为本书的目的是让读者检验这些思想在 代码生产中的应用而非向读者介绍这些思想本身。我们对这些材料进行了组织安排,以便读 者能够从基础的内容开始逐步进阶到复杂的部分。但是,由于该书是一本教科书,而非一本 侦探题材的小说,因此读者可以根据自己的兴趣自由地安排阅读的顺序。 1.2.1 排版约定 所有的代码清单和程序元素(例如函数名、关键词和操作符)的文字引用都被设为打字机 5 字体。一些例子引用了在UNIX或Windows命令行中执行的命令序列。我们用shell命令提示符 $来表示UNIX shell指令,用DOS命令提示符C:\>来表示Windows控制台提示符。UNIX shell命令 可以扩展到多行;以>为续行符。 $ grep -l malloc *.c | > wc -l 8 C:\>grep -l malloc *.c | wc -l 8 提示符和续行符仅用于区分输入与系统输出;实际操作时只输入提示符后面的命令。 本书有些地方讨论了不安全的编码实践或常见的陷阱。对于这些位置,在书的边白处以危险 符号进行了标识。在进行代码走查,或只是阅读代码寻找 bug 时,应该警惕这类代码。在书 的边白处,以 i 符号标出的文本表示了编码的习惯用法。在阅读文章时,我们都是识别整个 单词而非字母;同样,识别代码中的习惯用法可以让你更快、更高效地阅读代码,并从更高 的层次来理解程序。 本书中使用的代码示例都来自于现实中的程序。在脚注中标明了所使用的程序(如图 1-1 所 示)6,给出了该程序在本书配套的源代码的目录树中的精确位置,以及具体代码片段的起 止行号。如果图中包括不同源码文件的部分(参见图 5-17),则脚注将指出这些文件所在的 目录 7。 main(argc, argv) [...] { if (argc > 1) for(;;) (void)puts(argv[1]); else for (;;) (void)puts("y"); } 5 英文原版如此,翻译版本中略有调整。——编者注 6 netbsdsrc/usr.bin/yes/yes.c:53–64 7 netbsdsrc/distrib/utils/more 简单注解 省略的代码 1 正文引用的注解 图 1-1 含有注解的程序清单 有时,我们会省略所列代码的某些部分,并以省略号标志[...]标明。在这些情况下,起止行 号代表所列代码覆盖的整个范围。参考回原来的代码,你可能会注意到,其他方面的改变包 括:大部分C程序的声明从老版的“Kernighan和Ritchie”风格转到了ANSI C风格,并且省略 掉了注释、空格,以及程序许可信息。我们希望,这些变更在不过分影响原示例真实性的基 础上,可以增加所提供示例的可读性。重要的代码示例是利用一个定制的软件应用程序通过 图形化的方式进行注解的 8。使用注解软件确保了不会破坏示例的正确性,可以进行计算机 验证。有时,我们会用叙述性文字来展开一段注解。在这些情况下(图 1-1:1),注解以一个 印在方框中的数字开始;正文中使用相同的数字——跟在冒号后面——来引用该注解。 1.2.2 图表 我们选择UML来作为我们的设计图,这是因为它已经是事实上的行业标准。在准备本书的过 程中,我们发现,开发一种用来生成UML图的开源的解释性语言 9是很有用的。同时,我们 也对GraphViz10工具的底层代码库做了一些小的改进。我们希望生成的UML图有助于更好地 理解所分析的代码。 图 1-2 给出了在我们的图示中用到的一些标记的例子。请记住以下几点:  采用 UML 活动类的记法——边框加粗的类框(参见图 6-14)——来绘制过程(如过 滤器风格的程序)。  采用关联导航关系——带有开放型箭头的实线——来描绘数据元素之间的指针。我 们还将每个数据结构划分成水平或垂直的间隔区,以便更好地描绘内部的组织结构 (参见图 4-10)。  用关联线上的实心箭头来表示关联的方向(例如,说明数据的流向),而非按 UML 所规定的那样在关联线的顶部(参见图 9-3)。 所有其他的关系都使用标准的 UML 标记。  类继承的关系绘制为泛化关系——带有中空箭头的实线(参见图 9-6)。  接口的实现用实现关系来表示——带有中空箭头的虚线(参见图 9-7)。  两个元素之间的依赖关系(例如,编译过程中文件之间的关系)用带有开放型箭头 的虚线来表示(参见图 6-8)。  组合(例如,由各种模块组成的库)通过聚联来描绘——线的一端为菱形(参见图 9-24)。 8 英文原版使用 TeX 排版,翻译版本因排版环境改变而略有不同。——编者注 9 http://www.spinellis.gr/sw/umlgraph 10 http://www.graphviz.org 图 1-2 基于 UML 的图示标记法 1.2.3 练习 在大多数节的最后,都可以找到一些练习,这些练习旨在激发你的兴趣来应用一下所介绍的 技术,并在某些令你感兴趣的话题上做进一步的研究,或者也可以作为更深一步讨论的起点。 在大部分例子中,你可以交替参照本书配套光盘中的内容和“你开发环境中的实际代码”。 重要的是阅读并检查来自现实世界中那些非凡系统的代码。如果你当前正在这样一个系统上 工作,那么将目标放在该系统上而非本书随附的光盘上,代码阅读的成效将更为显著。 许多练习都是从让你定位特定的代码段开始的。该任务可以自动完成。首先,将要寻找的代 码表示为一个正则表达式。(关于正则表达式方面的更多内容参见第 10 章。)然后,以 UNIX 环境为例,在代码库中使用如下指令进行查找: find /cdrom -name '*.c' -print | xargs grep 'malloc.*NULL' 或者在Windows环境下使用Perl脚本codefine.pl11。(源代码库中的某些文件和老式的MS-DOS 设备同名,会导致在访问它们时某些版本的Windows实现挂起;Perl脚本显式地绕开了这个 问题。) 1.2.4 辅助材料 本书所有例子都基于现有的开源软件代码。源代码库包含超过 53000 个文件,大小超过 540MB。所有对代码示例的引用都已在脚注中明确标出,以便可在原来的环境中分析这些被 引用的代码。此外,还可以通过 3 种不同的方式,将源代码库与本书的正文对应起来。 1. 在索引中查找每个被引用的源文件的名字(文件全路径的最后部分)。 2. 浏览附录 A,这里提供了一个源代码库的概况。 3. 搜索附录 C,附录 C 中包含有一个被引用的源代码文件的清单,该清单是按照代码 目录结构来排序的。 11 tools/codefind.pl 1.2.5 工具 提供的示例中,有一些是以类 UNIX 操作系统中的某些程序(比如 grep 和 find)可用为前提 的。大量的此类系统(例如,FreeBSD、GNU/Linux、NetBSD、OpenBSD 和 Solaris)现在都可 以免费下载,并可被安装到许多种硬件平台上。如果你使用的不是这类系统,通过利用已经 完成的对这些工具的移植(port),同样可以从这些工具中获得便利。(10.9 节包含有工具可 用性方面的更详尽信息。) 1.2.6 提纲 在第 2 章中,我们给出了两个完整的程序,并一步一步地检查它们的工作方式。为此,我们 概述了一些基本的代码阅读策略,并标识出了常见的 C 语言控制结构、构建块、习惯用法和 缺陷。C 语言中一些更为高级(同时也易于误用)的内容则放到第 3 章和第 5 章讨论。第 4 章分析如何阅读代码,收录了常见的数据结构。第 6 章针对那些真正的大型项目中的代码: 分布在不同地点的小组共同工作,包括数千个文件和数百万行代码。大型项目一般采用共同 的编码规范和约定(在第 7 章中讨论),并且可能包括正式的文档(在第 8 章中介绍)。第 9 章提供了背景信息,并且建议“不能只见树木不见森林”:系统的架构,而非代码细节。阅 读代码时,有大量的工具可供使用,这是第 10 章的主题。最后,第 11 章中包含了一个完整 的且已被解决了的例子,本书其他部分讲述的代码阅读和代码理解技术将被应用于在 NetBSD 的源代码库中定位并提取月相算法,并将其作为 SQL 函数加入到基于 Java 的 HSQL 数据库引擎中。 在本书的最后,以附录的形式概括了在示例中使用的代码和随书光盘中的代码(附录 A), 提供了本书代码的个人及组织名录(附录 B),一份关于所有被引用的源码文档的按目录中 的出现次序排列的清单(附录 C),源码的许可信息(附录 D),以及一份关于代码阅读的格 言清单(同时给出了所在页码)(附录 E)。 1.2.7 关于“伟大语言”的争论 本书的大部分示例都基于 C 程序,在 POSIX 字符终端环境下运行。这一选择背后的原因与开 源软件中可移植 C 代码的丰富性有关,也同 C 示例代码的简明性有关——与用 C++和 Java 编写的类似示例相比而言。(这一现象背后的原因,很有可能与代码的年代或流行的编码风 格有关,而非语言自身的特征。)很不幸,基于图形用户界面的程序很少出现在我们的例子 中,阅读与评说此类程序真的需要单独的一本书才能完成。书中在提及 Microsoft Windows API 函数时都是指 Win32 SDK API,而非.NET 平台。 我们已被反复问及关于用于编写开源软件的语言方面的问题。表 1.1 汇总了SourceForge.net 代码库中前 10 种最常使用的语言开发的项目数量。12C语言占据了列表顶部的重要位置,并 且可能还没有充分代表其所占的份额,因为许多非常大型的C语言的开源项目(例如FreeBSD 和GNU/Linux)都是被单独对待的(也就只能计入一条项目数量)。还有,许多项目声称使用 的是C++,但实际上是用C语言编写而成,只使用了很少的C++特性。另一方面,也要知道整 理一份非开源项目的类似列表可能会得到完全不同的结果,COBOL、Ada和Fortran,以及各 种 4GL语言可能会占据列表中顶部的位置。此外,如果你正在维护代码——阅读代码的一个 不太流行的理由——那么你将阅读的语言极有可能是 5(如果幸运的话)或 10 年前所采用 12 http://sourceforge.net/softwaremap/trove_list.php?form_cat=160 的语言,反映出编程语言的时代特征。 示例代码所表达的代码结构大部分情况下也适合于 Java 和 C++程序;有很多还适合于 Perl、 Python 和 PHP。尽管如此,在阅读时大可毫无顾忌地跳过下面这些内容:  如果你从未接触过 C(或 C++)代码,可以跳过第 3 章。  如果 C++和 Ada 不是你的菜,可以跳过 9.3.4 节。  如果你已经打算避免面向对象的解决方案、C++、Java、Python、Smalltalk,以及 Perl 中的面向对象特性,可以跳过 9.1.3 节。  如果你不涉及 Java 和 C++,可以跳过 5.2 节。  如果你是结构化编程的狂热者,或者 Java 的忠实拥趸,可以跳过 2.8 节。 表 1-1 开源项目中最常使用的 10 种语言 语言 C C++ Java PHP Perl Python Visual Basic UNIX Shell Assembly JavaScript 项目数量 8393 7632 5970 4433 3618 1765 916 835 745 738 占项目的百分比 21.2 19.2 15.1 11.2 9.1 4.5 2.3 2.1 1.9 1.9 进阶阅读 软件可以作为存储可执行知识的介质,和这一角色有关的内容在Armour[Arm00]中得以很好 地描述。Petzold的Code[Pet99]13一书介绍了计算机技术中使用的各种编码系统,该书可读性 强,且包罗万象。Kernighan和Plauger在[KP76]中倡导将认真研究和效仿优质程序作为提高代 码编写能力的一种方法。阅读和理解代码同样也是软件维护中不可缺少的一部分。Pressman 在[Pre00]的第 30 章对软件维护的开销这一话题进行了分析,更详尽的分析参见Boehm的 [Boe81]和[Boe83]。Glass在[Gla00]中向我们呈现了代码阅读在开源世界中的社会规模。Knuth 的“文学化编程(Literate Programming)”方法论中倡导代码应当为读而写。Bentley的 “Programming Pearls”14[BK86]栏目是关于这部分内容的一个更易读的介绍。而Knuth的作品 [Knu92]和他的两部文学化编程著作[Knu86a]、[Knu86b],以及一系列基于其最初思想的文章 13 中文版《编码:隐匿在计算机软硬件背后的语言》,左飞等译,电子工业出版社出版。——译者注 14 该栏目中的文章汇总后以《编程珠玑》为名出版,黄倩等译,人民邮电出版社。——译者注 [BKM86]、[Han87]、[Jac87]、[Ham88]、[WL89]提供了更多的例子。关于阅读老式计算机代 码的行为,最近已被描述在《Software Archeology》[HT02]中。 经过了 20 多年,现在Lions的著作已经有了不受限制的版本[Lio96]。Raymond在[Ray01]中介 绍了开源软件的开发过程。E1-Emam在[El-01]中讨论了将开源软件用做研究(和教学)材料 时应当遵循的道德标准。作为一个科学的通信传播手段,社会的进步已经造就了数学定理方 面的成功,DeMillo等著的[DLP77]中对此进行了介绍。面向方面的编程(Aspect-oriented Programming)对于软件维护人员来说是一个有价值的补充工具,请阅读Kiczales等所著的 [KHH+01]中关于AspectJ的介绍,以及该期刊中的其他相关文章。Lientz和Swanson在[LS80]中 对软件维护的管理(以及 1.1.4 节中使用的图表)进行了分析。Fowler在[Fow00]中介绍了更 多有关重构的内容。Beck撰写了极限编程方面的权威介绍[Bec00]。读者还可以在网络上找到 相应的敏捷软件开发宣言(Manifesto for Agile Software Development)15。[Kru92]和[MMM95] 概述了最重要的软件重用话题。[Spi99a]和[SR00]描述了基于开源代码开展进程级别的软件复 用的实例。Boehm等在[BCH+95]中首次提出了COCOMOⅡ软件成本模型。 15 http://www.AgileManifesto.org/

Top_arrow
回到顶部
EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。