4.11 程序开发的过程
经过了前面一段的学习,现在可以把程序开发的过程与步骤大致地总结一下了。开发程序大体上要经历下面一些主要步骤。
1.定义程序功能
这是必须且是最重要的工作,也是经常不被重视的步骤。这个阶段主要是分析、确定程序的功能。如果有必要,需要把这个程序的功能描述出来,越详细越具体越好。
描述一定要精确。可能的话,明确地写出程序各种可能的输入及相应的输出。这些是程序测试的依据。
正如永远不可能求解出一个错误的题目一样,有些程序要求是根本不可能完成的。比如编写程序求两个数的和。这种题目本身就存在问题,因为没有说清楚是两个什么样的数,是整数?分数?复数?还是小数?这样的程序根本就无法完成。
在现实的软件开发工作中,这个步骤的意义更为重要。其重要性甚至超过了后面一起工作重要性的总和。因为对这个阶段错误的修改,代价是最大的。这个步骤在软件工程中通常被称为“需求分析”。
这个阶段的工作有时是很不容易的,因为并非所有客户都能说清他们真正需要的是什么。但不能容忍的是程序设计人员自己也不清楚究竟要开发什么东西。比如,“判断m是否是素数”,就属于这种情况。因为没人清楚m是个什么东西。
2.设计数据结构和算法
在明确了程序的目的和要求之后,需要根据对问题的分析确定数据结构和算法。在目前的学习阶段,所谓的确定数据结构通常意味着选择适当的数据类型。
算法与数据结构是相辅相成的、互相配合的。片面地强调任何一方都无法开发出高质量的程序。有时,一种恰当的数据结构可以使算法非常简洁高效;反过来,任何一个算法必须在合适的数据类型或数据结构的基础上才能成为现实。
在思考算法时,对于不熟悉的问题,用笔、纸亲自算几步可能会有助于算法的归纳与总结。凡是自己用笔、纸都无法经过有限步骤完成的问题,根本还谈不上编程。
算法的描述可以使用流程图(flow chart)、N-S图(Nassi-Shneiderman Diagrams,NSD)或伪代码。“流程图”因为箭头转向较多,不利于结构化程序设计,目前已经较少使用。对于初学者,用NSD草拟算法是一种值得推荐的方法。此外,在编写代码前最好写出较详细的伪代码,这样可以减少代码中的失误,而且对这些伪代码稍加整理,就可以成为很不错的注释。
3.代码编辑
完成前面的工作后,就可以编写代码了。
编写代码时容易出现的问题主要是键入内容错误和思想表达方面的词不达意。前者可以通过良好的编程习惯尽量减少失误,而后者需要对C语法的深入理解和不断练习、逐步提高来克服。
代码的工整、美观有利于提高代码的质量。在一些小的程序中,这一点不容易被发现,但是一旦养成了不好的编码习惯,其弊端在日后总会显现并得到报应。所谓“勿以善小而不为,勿以恶小而为之”,便是深得编程实质的至理名言。
4.编译与链接
生成可执行文件并不是仅仅把源代码翻译成机器语言那么简单,实际上还包括把各个机器语言程序片段组装在一起并加上一些必要的其他信息等工作。后者叫做链接。
在编译过程中,编译器会检查代码中是否存在违背C语言语法的内容,并根据情况分别给出出错信息和警告信息。出错信息表明的是代码违背了C语言的语法格式,编译器对代码无法理解并表示困惑,编译无法继续,代码必须修改。而存在警告信息的情况下,编译虽然可以继续,但绝大多数情况下这些警告信息意味着潜在的错误。所以不可以放过警告。优秀的程序员一般是无法容忍自己的代码中存在警告信息这种瑕疵的,哪怕这种瑕疵并没有害处。对待警告信息,有“洁癖”体现的是程序员的洁身自好的优秀品质。
链接有时也不见得那么一帆风顺,那种以为编译通过之后就可以顺利链接的天真想法,毫无疑问是代码编写太少的缘故。实际上链接的错误有时更难排查。
5.测试与运行
这个阶段的主要任务是用“定义程序功能”阶段设想的输入与输出对程序进行测试。如果运行结果与事前设想一致,可以认为程序开发完成。
当发现程序运行与事先设想的情况不符时,在找到错误的原因后,必须返回之前的步骤中进行修改。
不应该等代码全部完成之后再进行测试,而应该每写出一部分,只要有可能,就进行测试。
6.调试
有时,程序运行结果与设想不同,但错误的原因很难发现。寻找并改正错误的过程就叫做调试(Debugging)。调试与其说是一种技术,倒不如说是一种艺术,有时经验与直觉的作用可能更大些。调试本质上是详细地观察程序的每一步运行过程,查看哪些与事先设想的相符合,哪些与事先设想的不符。较多的时候需要观察变量或表达式的值,技术上主要通过临时增加一些输出功能的语句,或借助IDE提供的一些便捷的手段实现。
7.维护
在程序交付使用后,依然可能有新的问题发生或产生对软件的修改要求。这时前面工作中产生的文档和记录将有非常重要的作用。对于本书的读者几乎不涉及这方面的问题。但是保留好源代码和相关的记录文档日后重新看看,对水平的提高是非常有益的。
程序开发总的过程大致如图4-6所示。图中的虚线表示产生相应的文件,实线表示操作步骤。图中没有明确画出哪些属于“调试”步骤,实际上任何返回开头查找错误并修改都属于调式的范畴。
图4-6 程序开发过程示意图