第一章 错误的分类
学会如何正确的将错误进行分类有利于错误排查,这让你能对错误进行精准定位,而不是大海捞针。
富有经验的程序员能够通过观察若干个现象快速甄别出错误发生的原因,进而有针对性地调试程序。
而菜鸟程序员,本身对语法就不是很熟悉,再加上没有定位错误的经验,很容易懵逼。
典型的现象如下:
- 编译错误越改越多。
- 看不懂或不看编译错误提示。
- 手足无措,被水淹没。
有没有觉得膝盖中了一箭?
第一章介绍如何根据基本的程序行为现象来分类错误,并提供深入查找问题解决方案的提示。
第一节 基本分类原则
我们通常在这些情况下认为一个程序产生了错误:
- 无法运行 (第二章到第三章)
- 运行时崩溃 (第四章)
- 结果和预期不符 (第五章)
对于逻辑正确的代码,本书可能会提供一些在代码结构方面更优雅的实现,但专门对于算法的调优不在本书的讨论范围之内。
下面对这三种情况进行讨论
第二节 无法运行
无法运行是指我们无法启动我们所编写的程序,当使用IDE或直接使用编译器时,同时会产生错误信息。
请注意在这种情况下会产生错误信息,这些错误信息对排查错误非常有帮助。
在懂得基本的英语的前提下,你会发现编译器的错误信息的含义很明确,这时利用你的智慧去理解这些信息,你将会很快地排除错误。
在这种情况下,编译器所产生的错误信息一般是错误(Error)级别的,这些错误又可以分成两类:
- 编译错误(见第二章)
- 链接错误(见第三章)
编译器会产生 Error(错误) 与 Warning(警告:非致命的错误) 两种级别的提示。
如果有 Error 提示, 将不会产生可执行文件,因而无法运行。
如果仅有 Warning, 会继续产生可执行文件,因而可以运行。
对于 Warning 的详细说明,参见第八章。
其中编译错误在编译(Compile)单个文件时发生,链接错误在构建(Build)工程或单纯链接(Link)文件时发生。
下面将举出一些常见的例子来帮助你分类这两类错误:
- 如果你只有一个文件且仅使用了标准库,编译错误。
- 如果出现unsolved symbol之类的错误,常常是链接错误。(一个特例是你仅仅声明了一个变量/函数却没有定义它,或位置不对)
- 如果错误代码里出现了Link字样,那是链接错误。
第三节 运行时崩溃
运行时崩溃本质上都是程序没有处理的异常。
这种情况产生的原因是程序进行了非法操作,比如:
- 对空指针解引用 (空指针异常)
- 访问非法内存 (段错误)
- 整数除0 (算术异常)
- 其他用户自定义的抛出异常(C++)
详细见第四章描述
第四节 结果和预期不符
Program testing can be used to show the presence of bugs, but never to show their absence!
程序测试只能证明错误的存在,但不能证明错误不存在!
—— E.W.Dijkstra ("Notes On Structured Programming", 1970) (计算机科学家, 1972年图灵奖得主)
切记不要轻易断言程序正确,即便:
- 编译通过
- 通过样例测试
- 通过大量数据测试
- 通过了某在线评测系统
数学,唯有数学证明,才能支撑程序的正确性而且屹立不倒。
程序的正确性与性能是两个完全不同的方面。
对于自动评测机来说,程序的性能也会被考虑其中,当程序正确时,你至少不会碰到 Wrong Answer (答案错误)
但你有可能仍然因为一些性能问题通不过评测。
因为它不仅要求程序正确,还要求程序高效。
即便程序是正确而且高效的,不代表代码是优秀的,参见第零章。
切记不要再提问中包括:“我的程序是正确的啊,怎么结果就不对呢?”之类的描述,关于提问详情参见第九章。
结果和预期不符往往是逻辑错误,详情参见第五章。