本文中的文字摘录于 Bjarne Stroustrup 编写的 《C++程序设计原理与实践》(中文出版时间 2010 年,英文原版 2009 年)中关于程序设计的一些观点见解。最开始我是在 2017 年 2 月底从学校的图书馆中找到了这本书,当时正在准备 CCF CSP 的程序设计考试,以达到学院对我们毕业的基本要求。在翻阅这本书时主要是看下大概,了解一些观点,因为当时我自己主要学的编程语言是 Java,而在查看这本书的过程中,我把一些令我印象较深的地方用手机拍了下来,这两天把当时拍得十几张照片中的内容汇整了一下,形成了这篇文章。
(一)
程序设计是这样的一门艺术,它将问题求解方案描述成计算机可以执行的形式。程序设计中很多工作都花费在寻找求解方案以及对其求精上。通常,只有在真正编写程序求解一个问题的过程中才会对问题本身理解透彻。
本书适合于那些从未有过编程经验但愿意努力学习程序设计的初学者,它能帮助你理解使用 C++ 语言进行程序设计的基本原理并获得实践技巧。我的目标是使你获得足够多的知识和经验,以便能使用最新最好的技术进行简单有用的编程工作。达到这一目标需要多长时间呢?作为大学一年级课程的一部分,你可以在一学期内完成这本书的学习(假定你有另外四门中等难度的课程)。如果你是自学的话,不要期望能花费更少的时间完成学习(一般来说,每周 15 小时,共 14 周是合适的学时安排)。
三个月可能看起来是一段很长的时间,但要学习的内容很多,写第一个简单程序之前,就要花费一个小时。而且,所有学习的过程都是渐进的:每一章都会介绍一些新的有用的概念,并通过从实际应用中获取的例子来阐述这些概念。随着学习进程的推进,你通过程序代码表达思想的能力——也就是让计算机按你的期望工作的能力,会逐渐稳步地提高。我从不会说:「先学习一个月的理论知识,然后看看你是否能使用这些理论吧。」
为什么要学习程序设计呢?因为计算机文化是建立在软件之上的。如果不理解软件,那么你将退化到只能相信「魔术」的境地,并且将被排除在很多最为有趣、最具经济效益和社会效益的领域之外。当谈论程序设计时,我所想到的是整个计算机程序设计家族,从带有 GUI(图形用户界面)的个人计算机程序,到工程计算和嵌入式系统控制程序(如数码相机、汽车和手机中的程序),以及文字处理程序等,在很多日常应用和商业应用中都能看到这些程序。程序设计与数学有些相似,如果认真去做的话,它会是一种非常有用的智力训练,可以锻炼我们的思考能力。然而,由于计算机能做出反馈,程序设计又不像大多数数学形式那么抽象,因而对更多的人来说更容易接受。可以说,程序设计是一条能够打开你眼界,将世界变得更美好的途径。最后,程序设计非常有趣。
(二)
如果你只是希望直接使用别人编写的程序,而不想了解其内部原理,也不想亲自向代码中加入重要的内容,那么本书不适合你。请考虑是否采用另一本书或另一种程序设计语言会更好些。如果这大概就是你对程序设计的看法,那么请考虑一下你从何得来的这种观点,它真的满足你的需求吗?人们常常低估程序设计的复杂程度和它的重要性。我不愿意看到你不喜欢程序设计,只是因为你的需求与我所描述的部分软件之间不匹配。信息技术中还有很多部分是不要求程序设计知识的,那些领域可能适合你。本书面向的是那些确实希望编写和理解复杂计算机程序的人。
考虑到本书的结构和注重实践的特点,它也可以作为程序设计方面的第二本书,适合那些已经了解一点点 C++ 的人,和哪些会用其它语言编程,现在想学习 C++ 的人。如果你属于其中一类,我不好估计你学习这本书要花费多长时间。但我可以给你的建议是,多做练习。因为你在学习中常见的一个问题是习惯用熟悉的、旧的方式编写程序,而不是在适当的地方采用新技术,多做练习会帮助你解决这个问题。如果你曾经按某种更为传统的方式学习过 C++,那么在进行到第 7 章之前,你会发现一些令你惊奇的和有用的内容。除非你的名字是 Stroustrup,否则你会发现我在本书中所讨论的内容不是「你父辈的 C++」。
学习程序设计要靠编程实践。在这一点上,程序设计与其他需要实践学习的技能时相似的。你不可能仅仅通过读书就学会游泳、演奏乐器、或者开车,你必须进行实践。同样,不读程序、不写程序就不可能学会程序设计。本书给出了大量代码示例,都配合有说明文字和图表。你需要通过读这些代码来理解程序设计的思想、概念和原理,并掌握用来表达这些思想、概念和原理的程序设计语言的特性。但有一点很重要,仅仅读代码是不能学会编程实践技巧的。为此,你必须进行编程练习,通过编程工具熟悉编写、编译和运行程序。你需要亲身体验编程中会出现的错误,学习如何修改它们。总之,在学习程序设计的过程中,编写代码的练习是不可替代的。而且,这也是乐趣所在!
另一方面,程序设计远非只是遵循一些语法规则和阅读手册那么简单。本书的重点不在于 C++ 的语法,而在于理解基础思想、原理和技术,这是一名好程序员所必备的。只有设计良好的代码才有机会成为正确、可靠和易维护的系统的一部分。而且,「基础」意味着延续性:当现在的程序设计语言和工具演变甚至被取代后,这些基础知识仍会保持其重要性。
那么计算机科学、软件工程、信息技术等又如何呢?它们都属于程序设计的范畴吗?当然不是!但程序设计是一门基础性的学科,是所有计算机相关领域的基础,在计算机科学领域占有重要的地位。本书对算法、数据结构、用户接口、数据处理和软件工程等领域的重要概念和技术进行了简要介绍。但本书不能取代对这些领域全面、均衡的学习。
(三)
致学生:
到目前为止,我在德州农工大学已经用本书的初稿教过 1000 名以上的大一新生,其中 60% 曾有过编程的经历,而剩余 40% 从未见过哪怕一行代码。大多数学生的学习是成功的,所以你也可以成功。
你不一定是在某门课程中来学习本书,我认为本书会广泛用于自学。然而,不管你学习本书是作为课程的一部分还是自学,都要尽量与他人协作。程序设计有一个不好的名声——它是一种个人活动,这是不公正的。大多数人在作为一个共有目标的团体一份子时,工作效果更好,学习得更快。与朋友一起学习和讨论问题不是作弊!而是取得进步最有效,同时也最快乐的途径。如果没有特殊情况的话,与朋友一起工作会促使你表达出你的思想,这正是测试你对问题理解和确认你的记忆的最有效的方法。你没有必要独自解决所有编程语言和编程环境中的难题。但是,请不要自欺欺人,不去完成那些简单练习和大量习题(即使没有老师督促你,你也不应这样做)。记住,程序设计(尤其)是一种实践技能,需要通过实践来掌握。如果你不编写代码(完成每章的若干习题),那么阅读本书纯粹就是一种无意义的理论学习。
大多数学生,特别是那些爱思考的好学生,有时会对自己努力工作是否值得产生疑问。当(不是如果)你产生这样的疑问时,休息一会儿,重新阅读这篇前言,阅读一下第 1 章(计算机、人和程序设计)和第 22 章(思想和历史)。在那里,我试图阐述我在程序设计中发现了哪些令人兴奋的东西,以及为什么我会认为程序设计是能为世界带来积极贡献的重要工具。如果你对我的教学理念和一般方法有疑问,请阅读第 0 章(致读者)。
你可能会为本书的厚度感到担心。本书如此之厚的一部分原因是,我宁愿反复重复一些解释说明或增加一些实例,而不是让你自己到处找这些内容,这应该令你安心。另外一个主要原因是,本书的后半部分时一些参考资料和补充资料,供你想要深入了解程序设计的某个特定领域(如嵌入式系统程序设计、文本分析和或数值计算)时查阅。
还有,学习中请耐心些。学习任何一种重要的、有价值的新技能都要花费一些时间,而这是值得的。
(四)
讲授和学习本书的方法:
我们是如何帮助你学习的?又是如何安排学习进程的?我们的做法是,尽力为你提高编写高效的实用程序所需要的最基本的概念、技术和工具,包括:
- 程序组织;调试和测试;类设计;计算;函数和算法设计;绘图方法(仅介绍二维图形);图形用户界面(GUI);文本处理;正则表达式匹配;文件和流输入/输出(I/O);内存管理;科学/数值/工程计算;设计和编程思想;C++ 标准库;软件开发策略;C 语言程序设计技术;
认真完成这些内容学习,我们会学到如下程序设计技术:过程式程序设计(C 语言程序设计风格)、数据抽象、面向对象程序设计和泛型程序设计。本书的主题是程序设计,也就是表达代码意图所需的思想、技术和工具。C++ 语言是我们的主页工具,因此我们比较详细地描述了很多 C++ 语言的特性。但请记住,C++只是一种工具,而不是本书的主题。本书主题是「用 C++ 语言进行程序设计」而不是「C++ 和一点程序设计理论」。
我们介绍的每个主题都知道服务于两个目的:提出一种技术、概念或原理,介绍一种实用的语言特性或库特性。例如,我们用一个二维图形绘制系统的接口展示如何使用类和继承。这使我们节省了篇幅(也节省了你的时间),并且还强调了程序设计不只是简单地将代码拼装起来以尽快得到一个结果。C++标准库是这种「双重作用」例子的主要来源,其中很多主题甚至具有三重作用。例如,我们会介绍标准库中的向量类 vector,用它来展示一些广泛使用的设计技术,并展示很多用 vector 的程序设计技术。我们的一个目标是向你展示一些主要的标准功能库是如何实现的,以及它们如何与硬件相配合。我们坚持认为一个工匠必须了解他的工具,而不是仅仅把工具当作有「魔力的东西」。
对于程序员来说,总是会对某些主题比其它主题更感兴趣。但是,我们建议你不要预先判断你需要什么(你怎么知道你将来会需要什么呢),至少每一章都要浏览一下。如果你学习本书是作为一门课程的一部分,你的老师会指导你如何选择学习内容。
我们的教学方法可以描述为「深度优先」,也可以描述为「具体优先」和「基于概念」。首先,我们快速地(好吧,是相对快速的,从第 1 章到第 11 章)将一些编写小的实用程序所需要的技巧提供给你。在这期间,我们还简明扼要地提出很多工具和技术。我们着重介绍简单具体的代码实例,因为相对于抽象概念,人们能更快地领会到具体实例,这是大多数人的学习方法。在最初阶段,你不应期望理解每个小的细节。特别是你会发现,对刚刚还工作得好好的程序知识稍加改动,便会呈现出「神秘」的效果。尽管如此,你还是要尝试一下!还有,请完成我们提供的简单练习和习题。请记住,在学习初期你只是没有掌握足够的概念和技巧来准确判断什么是简单的,什么是复杂的;请等待一些惊奇的事情发生,并从中学习吧。
我们会快速通过这样一个初始阶段——我们想尽快可能快地带你进入编写有趣程序的阶段。有些人可能会质疑,「我们的进展应该慢些、谨慎些,我们应该先学会走,再学跑!」但是你见过小孩儿学习走路吗?小孩儿确实是在学会平稳慢慢走路之前就开始自己学着跑了。与之相似,你可以先勇猛向前,偶尔摔一跤,从中获得编程的感觉,然后再慢下来,获得必要的精确控制能力和准确的理解。你必须在学会走之前就开始跑!
你不要投入大量的精力试图学习一些语言或技术细节的所有相关内容。例如,你可以熟记所有 C++ 的内置类型及其使用规则。你当然可以这么做,而且这么做会使你觉得自己很博学。但是,这不会使你成为一名程序员。如果你学习中略过一些细节,将来可能偶尔会因为缺少相关知识而被「灼伤」,但这是获取编写好程序所需的完整知识结构的最快途径。注意,我们的这种方法本质上就是小孩儿学习母语的方法,也是教授外语的最有效的方法。有时你不可避免地被难题困住,我们鼓励你向授课老师、朋友、同事、辅导员以及导师等寻求帮助。请放心,在前面这些章节中,所有内容本质上都不困难。但是,很多内容都是你所不熟悉的,因此最初可能会感觉有点难。
随后,我们想你介绍一些入门技巧,来拓宽你的知识和技巧基础。你通过实例和习题来强化理解,我们为你提供一个程序设计的概念基础。
我们重点强调思想和原理。思想能指导你求解实际问题——可以帮助你知道在什么情况下问题求解方案是好的、合理的。你还应该理解这些思想背后的原理,从而理解为什么要接受这些思想,为什么遵循这些思想会对你和使用你的代码的用户有帮助。没有人会满意「因为事情就是这样的」这种解释。更为重要的是,如果真正理解了思想和原理,你就能将已有的知识推广到新的情况,就能用新的方法将思想和工具结合来解决新的问题。知其所以然是学会程序设计所必须的。相反,仅仅不求甚解地记住大量规则和语法特性有很大的局限,是错误之源,也是在浪费时间。我们认为你的时间很珍贵,尽力不浪费它。
我们把很多 C++ 语言层面的技术细节放在了附录和手册中,你可以随时按需查找。我们假定你有能力查找到你需要的信息,你可以借助索引和目录来查找信息。不要忘了编译器和互联网也有在线帮助功能。但要记住,要对所有互联网资源保持足够高的怀疑,直至你有足够的理由相信它们。因为很多看起来很权威的网站实际上是由程序设计新手或者想要出售什么东西的人建立的。而另外一些网站,其内容都是过时的。
(五)
程序设计和计算机科学:
程序设计就是计算机科学的全部吗?答案当然是否定的!我们提出这一问题的唯一原因就是确实曾有人将其混淆。本书会简单设计计算机科学的一些主题,如算法和数据结构,但我们的目标还是教授程序设计:设计和实现程序。这比广泛接受的计算机科学的概念更宽,但也更窄:
- 更宽,因为程序设计包含很多专业技巧,通常不能归类于任何一种科学。
- 更窄,因为就我们涉及的计算机科学的内容而言,我们没有系统地给出其基础。
本书的目标是作为一门计算机科学课程的一部分(如果成为一个计算机科学家是你的目标的话),作为软件构造和维护领域的第一门基础课程(如果你希望成为一个程序员或软件工程师的话),总之是更大的完整系统的一部分。
本书自始至终都依赖计算机科学,我们也强调基本原理,但我们是以理论和经验为基础来讲授程序设计,是把它作为一种实践技能,而不是一门科学。
创造性和问题求解:
本书的首要目标是帮助你学会使用代码表达你的思想,而不是教你如何获得这些思想。沿着这样一个思路,我们给出很多实例,展示如何求解问题。每个实例通常先分析问题,随后对求解方案逐步求精。我们认为程序设计本身是问题求解的一种描述形式:只有完全理解了一个问题及其求解方案,你才能用程序来正确表达它;而只有通过构造和测试一个程序,你才能确定你对问题和求解方案的理解是完整的、正确的。因此程序设计的本质是理解问题和求解方案工作的一部分。但是,我们的目标是通过实例来说明这一切,而不是通过「布道」或是对问题求解详细「处方」的描述。
(六)
程序员是孤立的这一神话本身只是一个神话而已。那些喜欢工作在自己选择的工作领域的人,经常痛苦地抱怨被「打扰」或开会的次数。由于现代软件开发是一种团队行为,因此那些喜欢和别人交互的人会感到轻松。这意味着社会和沟通能力是必不可少的,并且其价值远远大于陈规旧习。在一份对程序员很有用的技能的简短列表(你是在从现实的角度定义程序员),你会发现与不同背景的人进行沟通的能力最重要,这种沟通可能是非正式的会议、书面形式和正式介绍。我们相信,除非你完成过一个或两个团队项目,否则你不会知道什么是编程以及是够喜欢它。我们喜欢编程的理由是我们遇到的都是很好的、有趣的人,并且将访问风格各异的地方作为我们职业生涯的一部分。
所有这些是指那些有各种各样的技能、兴趣和工作习惯的人,对开发一个好的软件来说是必不可少的。我们的生活质量(有时甚至是生活自身)依赖于那些人。没有人可以扮演我们这里提到的所有角色,也没有明智的人希望扮演每个角色。重点是你有比自己所能想到的更大的选择空间,而不是不得不做出某个特定的选择。作为个人,你将「流向」那些符合你的技能、才智和兴趣的工作领域。
我们谈论的是「程序员」与「编程」,但是编程很明显只是整个画面的一部分。那些设计船只或移动电话的人不会将自己做程序员。编程是软件开发中的一个重要部分,但并不是全部。相似地,对于大多数的产品来说,软件开发是产品开发中的一个重要部分,但并不是全部。
我们并不假设你(我们的读者)希望成为一个专业程序员,将剩余的工作生涯用于编写代码。即使是那些优秀的程序员,他们也不会将大部分时间用在编写代码上。理解问题需要占用更多的时间,并且通常消耗智力。智力挑战时很多程序员认为编程有趣的出发点。很多优秀的程序员通常不是计算机科学专业的。例如,如果你进行基因研究方面的软件开发,理解分子生物将会对你有更大的帮助。如果你进行中世纪文学分析方面的程序设计,阅读一些这类文学作品和掌握一门或多门相关语言将会对你有更大的帮助。特别是对于抱有「只关心计算机和编程」态度的人,他们将难以与那些非程序员的同事交流。这样的人不仅会错过人与人交流(即生活)中最棒的那部分,而且也不会成为成功的软件开发人员。
于是,我们如何假设呢?编程是一种挑战智力的技能,需要经过很多重要与有趣的训练。另外,编程是我们这个世界的重要组成部分,不了解基本的编程知识就像不了解基本的物理、历史、生物和文学知识一样。那些对编程完全无知的人相信它是魔术,让这样的人来承担很多技术工作时危险的。另外,编程可以带来乐趣。
但是,我们如何假设编程的用途?你也许将编程作为未来学习和工作的重要工具,而不是成为一个专业的程序员。你也许将与他人进行专业和个人的交流,这些人可能是设计师、作家、经理或科学家,具有编程方面的基础知识将会有一定的优势。你也许将专业水平的编程作为你学习和工作的一部分。即使你成为一个专业的程序员,也不意味着你除了编程之外不做任何事。
你可能成为一名计算机或计算机科学方面的工程师,但是这样也并不是「编程占据所有时间」。编程使用代码表达你的思想的方式,也是一种协助求解问题的方式。除非你有值得表达的思想和值得解决的问题,否则编程没有用处(纯粹是浪费时间)。
这是一本关于编程的书,我们曾经承诺本书可以帮助你学习如何编程,那么我们为什么要强调非编程的内容与编程的有限作用呢?一个优秀的程序员会理解代码和编程技术在一个项目中的作用。一个优秀的程序员(在多数情况下)是一个优秀的团队成员,并且会努力理解代码和其产品如何很好地支持整个项目。例如,想象我在为一个新的 MP3 播放器进行编程,则我关心的知识代码优美程度和提供的简洁功能的数量。我可能一直在大型的、功能强大的计算机上运行这些代码。我可能对声音编码理论不屑一顾,因为它是「与编程无关的」。我将会待在自己的实验室里,而不是走出去与潜在的用户交流。这样,毫无疑问将对音乐有不好的体验,并且不欣赏图形用户界面(GUI)编程的最新发展。这样做的后果是可能对项目带来一场灾难。更大的计算机意味着更昂贵的 MP3 播放器和更短的电池寿命。编码是数字化音乐控制的重要部分,忽视编码技术的发展会导致每首歌曲增加所需的存储空间(不同编码获得相同品质的输出时的存储空间大小差异超过 100%)。无视用户的喜好(在你看来是奇怪的和过时的),通常会导致用户选择其它产品。编写一个好的程序的重要部分是理解用户的需求,并且在执行(即编码)时实现这些需求。为了完成上述对一个坏程序员的描绘,我倾向于将其描述成对细节的狂热和对简单测试的代码正确性的过分自信。我们鼓励你成为一个好的程序员,只是具有广阔的视野才能生产出好的软件。这既是社会价值也是个人自我实现之所在。
计算机科学:
即使是在最广泛的定义中,也最好将编程看做某些更大事物的一部分。我们可以将编程看做计算机科学、计算机工程、软件工程、信息技术或其他软件相关的学科。我们将编程看做科学工程中的计算机和信息领域的实现技术,同样是物理学、生物学、医学、历史学、文学和其他学术或研究领域的实现技术。
请思考计算机科学。在 1995 年,美国政府的「蓝皮书」对它的定义如下:「对计算系统和计算的系统研究。这个学科造就的知识体系包含理解计算系统和方法的理论,设计方法学、算法和工具,测试概念的方法,分析和验证的方法,以及知识的表示和实现。」正如我们预料的那样,维基百科条目所给出的概念不太正式:「计算机科学或计算科学是对信息和计算的理论基础的研究,以及它们在计算机系统中的实现和应用。计算机科学包含了很多子领域,有些强调特定结果的计算(例如计算机图形学),另一些关于计算问题的性能(例如计算复杂度理论)。有些集中在实现计算的挑战上。例如编程语言理论研究描述计算的方法,而计算机编程使用特定的编程语言来解决特定的计算问题。」
编程是一种工具。它是一种针对基础和实践问题的基本工具,使这些问题可以通过实验来测试、改进和应用。编程是思想和理论的实际交汇。这是计算机科学可以成为一种实践训练而不是纯理论,并且影响世界的原因所在。在这方面和很多其他事情一样,编程必不可少的是训练和实践的良好结合。它不应退化为这样一种活动:只是编写一些代码,用旧的方式满足眼前的简单需求。
(七)
程序员的理想:
我们希望从自己的程序中获得什么?我们通常(而不是从特定程序的特定功能)希望获得什么?我们希望保证正确性和作为其中一部分的可靠性。如果程序没有按照设想工作,没有按我们可以信赖的方式工作,小则是一个严重干扰、大则是一个危险。我们希望它得到良好的设计,这样它可以很好地满足实际需要;如果它所做的事情与我们无关,或者以某种我们厌烦的方式完成,则不能说程序是正确的。我们希望可以负担得起;我可能喜欢用 Roll-Royce 汽车或行政专机作为日常交通工具,但是除非我是一名亿万富翁,否则开销会影响我的选择。
这些方面是软件(工具、系统)能得到外部的、非程序员的赞赏所在。如果我们希望开发出成功的软件,那么这些内容必须成为程序员的理想,我么必须将它们永远记在心中,特别是在程序开发的早期阶段。此外,我们必须关注与代码本身相关的理想:我们的代码必须是可维护的,那些没有编写它的人可以理解它的结构和进行修改。一个成功的程序可以「生存」很长时间(通常有几十年)并经过反复修改。例如,它将会移植到新的硬件上,它将会增加新的功能,它将会修改以使用新的 I/O 设备(屏幕、视频、音频),它将会用新的自然语言进行交互等。只有失败的程序才永远不会被修改。为了保证可维护性,一个程序必须只与它的需求相关,它的代码必须直接体现要表达的思想。复杂性是简单性和可维护性的敌人,它对程序来说可能是必需的(在那种情况下我们不得不处理它),但是也可能是由于没有用代码清晰地表达出思想而产生。我们必须通过良好的编码风格来尽量避免它——风格是很重要的!
这听起来不太难,但是做起来确实很难。为什么?编程基本上是简单的:就是告诉机器你打算做什么。但是,为什么编程中要面对很多挑战?计算机基本上是简单的,它只能做很少几种操作,例如两个数相加和基于两个数的比较来选择要执行的下一条指令。问题是我们并不希望计算机做简单的事情。我们希望「机器」帮助我们做那些难以完成的事,但是计算机是挑剔的、无情的和不会说话的东西。另外,这个世界要比我们所相信的更复杂,因此我们并不能理解自己需求的实际含义。我们只希望一个程序能够「像这样做一些事情」,但是我们并不希望被技术细节所困扰。我们通常假设「基本常识」。不幸的是,人们认为很普通的基本常识,在计算机中通常完全不存在(通过某些精心设计的程序,可以在具体的、很好理解的情况下模拟它)。
这种思路导致的想法是「编程就是理解」:当你需要编写一个任务时,你需要理解它。相反,当你彻底理解一个任务,你可以编写程序执行它。换句话说,我们可以将编程看做努力去彻底理解一个课题的一部分。程序是我们对一个课题的理解的精确表示。
当你在进行编程时,你会花费很多时间尝试理解你试图自动化的任务。
我们可以将描述开发程序的过程分为四个阶段:
- 分析:问题是什么?用户想要做什么?用户需要什么?用户可以负担什么?我们需要哪种可靠性?
- 设计:我们如何解决问题?系统的整体结构将是怎样?系统包括哪些部分?这些部分之前如何通信?系统与用户之间如何通信?
- 编程:用代码表达问题(或设计)求解的方法,以满足所有约束(时间、空间、金钱、可靠性等)的方式编写代码。保证这些代码时正确的和可维护的。
- 测试:系统化地尝试各种情况,保证系统在所要求的所有情况下都能正确工作。
编程和测试相加通常称为实现。很明显,将软件开发简单分为四个部分是一种简化。分别针对这四个主题编写的书都很厚,并且很多书仍在讨论它们之间的关系。需要说明的一件重要的事情是开发的这四个阶段并不是独立的,并且不一定严格按照顺序依次出现。我们通常从分析开始,但是通过测试的反馈有助于对编程的改进;编程工作带来的问题可能表明设计带来的问题;按设计进行工作可能发现在设计中至今仍被忽视的某些方面的问题。系统的实际使用通常会暴露分析中的一些弱点。
这里的关键概念是反馈。我们从经验中学习,根据学到的东西改变我们的行为。这是高效软件开发的根本。对于很多大的项目,我们在开始之前不可能理解有关问题的所有事情和解决方案。我们可以尝试自己的想法和从编程中得到反馈,但是在开发的早期阶段更容易(更快)从设计方案的书写、按设计思路编程和朋友的使用中得到反馈。我们知道的最好的设计工具是黑板。你要尽可能避免独立设计。在已将设计思路解释给其他人之前,不要开始进行编码工作。在接触键盘之前,与朋友、同事、潜在用户等讨论设计和编程技术。从简单尝试到阐明思路的过程中,你所学到的东西是令人惊讶的。最终,程序只不过是对某些思路的表达(用代码)。
同样,当你实现一个程序时遇到问题,将目光从键盘上移开。考虑一下问题本身,而不是你的不完整方案。与别人交流:解释你希望做什么和为什么它不工作。令人惊讶的是,你向有些人详细解释问题的过程中经常会找到解决方案。除非不得已,否则不要单独进行调试(找到程序错误)!
本书的重点是实现,特别是编程。我们不讲授「解决问题」,以及提供有关问题的足够例子和它们的解决方案。很多问题的解决是认识到一个已知的问题,以及使用一个已知的解决方案。只有当大多数子问题以这种方式解决后,你才可能专注于令人兴奋的和有创造性的「跳出固有模式的思维」。因此,我们重点介绍如何用代码明确表达思路。
用代码直接表达思路是编程的基本理想。这确实是很明显,但是至今我们还缺少好的例子。我们将会反复回到这里。当我们需要在自己的代码中使用一个整数时,我们将它保存在一个 int 类型中,它会提供基本的整数操作。当我们需要使用一个字符串时,我们将它保存在一个 string 类型中,它会提供基本的文本控制操作。在最基本的层次上,理想是当我们有一个思路、概念和实体时,即那些我们可以作为「事情」考虑的、可以写在黑板上的、可以加入讨论的、(非计算机科学)教科书中讨论的东西,我们需要这些东西在程序中作为一个命名实体(类型)存在,并且提供我们需要它们执行的操作。如果我们需要进行图形处理,则需要一个 Shape 类型、一个 Circle 类型、一个 Color 类型和一个 Dialog_box 类型。当我们处理来自比如说一个温度传感器的数据流时,我们需要一个 istream 类型(「i」表示输入)。很明显,每种类型将提供适当的操作,并且只提供适当的操作。这些只是本书中提到的几个例子。基于上述内容,我们提供用于构建自己的类型的工具和技术,以便使用程序直接表达你希望体现的概念。
编程是实践和理论相结合的。如果你只重视实践,你将制造出不可扩展的、不可维护的程序。如果你只重视理论,你将制造出无法使用的(或无法负担的)玩具。
如果你想获得有关编程理想的不同类型的观点,以及少数在编程语言方面对软件做出重要贡献的人,请看第 22 章「理想与历史」。
(八)
思考题的目的是说明本章所解释的关键思想。可以把它们看做是练习的补充:练习关注的是编程的实践方面,而思考题尝试帮助你阐明思想和概念。在这方面,它们类似于好的面试题。
附录:
我们的文明运行在软件之上。软件是一种有趣的、对社会有益的、有利可图的工作,它是具有无与伦比的多样性和机会的领域。当你接触软件时,以有原则和严肃的方式工作:你要成为解决方案的一部分,而不是增加问题。
我们对贯穿技术文明的各种软件很敬畏。当然,软件并不是在所有应用中都表现良好,但那时另外一回事。在这里。我们想强调的是软件是多么普遍,以及我们在日常生活中多么依赖软件。它们都是由像我们这样的人来编写的。所有的科学家、数学家、工程师、程序员等,他们构建这里简略提到的软件的起点和你是一样的。
现在,让我们回到脚踏实地的业务上,学习那些编程需要的技术性的技能。如果你开始怀疑自己艰苦工作是否值得(大多数深思熟虑的人有时会怀疑它),你可以返回并重新阅读本章、前言和第 0 章。如果你开始怀疑自己是否能掌握这一切,请记住已经有数百万人成为称职的程序员、设计师、软件工程师等。你也可以做到。
(九)
程序设计要通过编写程序的实践来学习。 ——Brian Kernighan
在本章中,我们提出最简单的 C++ 程序,他们实际上可以做任何事。编写这些程序的目的如下:
- 让你试用自己的编程环境;
- 给你一个最初的体验:如何让计算机为你做某些事。
因此,我们提出程序的概念,使用编译器将程序从人类可读的形式转换到机器指令,以及最终执行机器指令的思想。
程序:
为了使计算机能够做某件事,你(或其他人)需要在繁琐的细节上明确告诉它怎么做。对「怎么做」的描述称为程序,编程是书写和测试这个程序的行为。
在某种意义上,我们以前都编写过程序。毕竟,我们曾描述过所要完成的任务,例如「如何开车去最近的电影院」、「如何找到楼上的浴室」、「如何用微波炉热饭」。这种描述和程序之前的不同表现在精确度上:人类往往通过常识对不明确的指示加以补偿,但是计算机不会这样。例如,「沿走廊右转,上楼,它就在你的左边」可能是对如何找到楼上的浴室很好的描述。但是,当你看到这些简单的指令时,你会在其中找到草率的语法和不完整的指令。人类很容易做出补偿。例如,假设你坐在桌子旁询问浴室的方向。你不需要被告知离开桌子来到走廊、绕过(不是跨过或钻过)桌子、不要踩到猫等。你不需要被告知不要带走刀子和叉子,以及记住打开灯才能看到楼梯。你也不需要被告知进入浴室之前首先要开门。
与此相反,计算机确实是不很笨的。它们做的所有事都要准确、详细的描述。我们考虑「沿走廊右转,上楼,它就在你的左边」。走廊在哪?什么是走廊?什么是右转?什么是楼梯?我如何上楼梯(每次迈一步?两步?沿扶手滑上楼梯?)什么是我的左边?它什么时候会在我的左边?为了向计算机精确描述这些「事情」,我们需要一种由特定语法精确定义的语言(英语对它来说有太多松散结构)和针对我们要执行的多种行动明确定义的词汇。这种语言称为编程语言,C++ 是为各种编程任务而设计的编程语言。
(十)
当你编程时,有时会对编译器感到相当懊恼。有时候,编译器会抱怨无关紧要的细节(例如缺少一个分号),或者一些你认为「明显正确」的东西。但是,编译器通常是正确的:当它给出一个错误消息并拒绝为你的源代码生成目标代码时,在你的程序中确实有不正确的地方,这意味着你写的程序不符合 C++ 标准的精确定义。
编译器没有常识(它不是人类),它对细节是非常挑剔的。由于编译器没有常识,因此你不能让它尝试这去猜测那些「看起来正确」但是不符合 C++ 定义的代码所表达的意思。如果你这样做并且编译器的猜测与你的不同,这时你需要花费很多时间找出为什么程序没有按你的要求工作。当所有的事都说清和做到后,编译器会帮你从大量自己造成的问题中解脱出来。编译器可能是你在编程时最好的朋友。
链接:
程序通常由几个单独的部分组成,它们经常由不同的人来开发。例如,“Hello, World!” 程序包含我们编写的部分和 C++ 标准类库。这些单独的部分(有时称为翻译单元)必须被编译,其目标代码必须被链接起来以形成一个可执行程序。用于将这些部分链接起来的程序通常称为链接器。
请注意目标代码和可执行程序是不能在系统之间移植的。例如,当你为一台 Windows 机器进行编译时,你得到的支持 Windows 的目标代码无法在 Linux 机器上运行。
库是一些代码的集合,它们通常是由其他人编写的,我们用 #include 文件中的声明来访问这些代码。声明用于指出一段程序如何使用一条语句。我们将在后面的章节(如 4.5.2 节)中详细介绍声明。
由编译器发现的错误称为编译时错误,由链接器发现的错误称为链接时错误,直到程序运行时仍未发现的错误称为运行时错误或逻辑错误。通常来说,编译时错误比链接时错误更容易理解和修正,链接时错误比运行时错误和逻辑错误更容易发现和修正。在第 5 章中,我们将详细讨论这些错误和它们的解决方式。
编程环境:
我们使用编程语言来编写程序。我们使用编译器将自己的源代码转换成目标代码,使用链接器将我们的目标代码链接成一个可执行程序。另外,我们使用一些程序在计算机中输入源代码文本并且编辑它。这些是最初的和最重要的工具,它们构成程序员的工具集合或「程序开发环境」。
如果你使用的是命令行窗口,就像很多专业程序员所做的那样,你将不得不自己来编写编译和链接命令。如果你使用 IDE(「交互式开发环境」或「集成式开发环境」),就像很多程序员所做的那样,简单地点击正确按钮就可以完成这个工作。附录 C 介绍了如何在你的 C++ 实现中编译和链接。
IDE 通常包括一个具有有用特性的编辑器,例如用不同颜色的代码来区分你的源代码中的注释、关键字和其他部分,以及其他帮助你来调试、编译和运行代码的功能。调试是发现程序中的错误和排除错误的活动,你在前进的道路上会听到很多有关它的内容。
在本书中,我们使用微软的 Visual C++ 作为编程开发环境实例。如果我们简单地说「编译器」或是 「IDE」的某些部分,那就是所指 Visual C++ 系统。但是,你可以使用一些提供最新的、符合标准的 C++ 实现的系统。我们所说的大多数内容(经过微小的修改)对所有的 C++ 实现都将是正确的,并且其代码可以在任何地方运行。在工作中,我们使用几种不同的实现。
简单练习:
迄今为止,我们讨论了很多有关编程、代码和工具(例如编译器)的内容。现在,你可以运行一个程序。这是本书中和学习编程过程中的一个重点。这是培养实践技能和好的编程习惯的开始。本章练习的重点在于使你熟悉软件开发环境。当你运行 “Hello, World!” 程序时,你将通过成为程序员的第一个重要的里程碑。
这个简单练习的目的是建立或加强你的实际编程技能,以及为你提供编程环境工具方面的经验。典型的简单练习是对一个独立程序的一系列修改,将琐碎的程序「发展」成一个实际程序的有用的部分。一套传统的练习是用于测试你的主动性、灵活性或创造性的。与此相反,简单练习很少需要发挥你创造力。典型的情况是顺序很关键,每个单独的步骤可能是容易(或琐碎)的。请不要自认为聪明地试图跳过某些步骤,这样通常会减慢你的进展或使你感到迷惑。
你可能会认为自己已经理解了阅读过的和导师或辅导员告诉你的任何事,但是重复和实践对于提高编程能力是很必要的。在这方面,编程和体育、音乐、舞蹈或任何基于技能的行业是相似的。请想象人们试图经过常规练习就能在这类领域中参与竞争,你知道结果会如何。坚持练习对专业人员意味着终身长期练习,是发展和维持一种高水平的实用技能的唯一方式。
因此,不要跳过这个简单练习,即使你多么想跳过去,它们从本质上来说是学习的过程。现在,从第一步开始并继续下去,测试每个步骤以确保你做得正确。
(十一)
今天的程序设计语言:
目前还在使用的程序设计语言有哪些,用于什么领域?这确实是一个难以回答的问题。当前语言的家族树——即便是以最简化的形式呈现,也很拥挤、杂乱,如下图所示:
版权声明:本文采用 CC BY-NC-SA 4.0 许可协议,若转载请注明出处。