编译时错误

    编译器只能翻译语法正确的程序,否则将导致编译失败,无法生成可执行文件。对于自然语言来说,一点语法错误不是很严重的问题,因为我们仍然可以读懂句子。而编译器就没那么宽容了,只要有哪怕一个很小的语法错误,编译器就会输出一条错误提示信息然后罢工,你就得不到你想要的结果。虽然大部分情况下编译器给出的错误提示信息就是你出错的代码行,但也有个别时候编译器给出的错误提示信息帮助不大,甚至会误导你。在开始学习编程的前几个星期,你可能会花大量的时间来纠正语法错误。等到有了一些经验之后,还是会犯这样的错误,不过会少得多,而且你能更快地发现错误原因。等到经验更丰富之后你就会觉得,语法错误是最简单最低级的错误,编译器的错误提示也就那么几种,即使错误提示是有误导的也能够立刻找出真正的错误原因是什么。相比下面两种错误,语法错误解决起来要容易得多。

    编译器检查不出这类错误,仍然可以生成可执行文件,但在运行时会出错而导致程序崩溃。对于我们接下来的几章将编写的简单程序来说,运行时错误很少见,到了后面的章节你会遇到越来越多的运行时错误。读者在以后的学习中要时刻注意区分编译时和运行时(Run-time)这两个概念,不仅在调试时需要区分这两个概念,在学习C语言的很多语法时都需要区分这两个概念,有些事情在编译时做,有些事情则在运行时做。

    逻辑错误和语义错误

    通过本书你将掌握的最重要的技巧之一就是调试。调试的过程可能会让你感到一些沮丧,但调试也是编程中最需要动脑的、最有挑战和乐趣的部分。从某种角度看调试就像侦探工作,根据掌握的线索来推断是什么原因和过程导致了你所看到的结果。调试也像是一门实验科学,每次想到哪里可能有错,就修改程序然后再试一次。如果假设是对的,就能得到预期的正确结果,就可以接着调试下一个Bug,一步一步逼近正确的程序;如果假设错误,只好另外再找思路再做假设。“当你把不可能的全部剔除,剩下的——即使看起来再怎么不可能——就一定是事实。”(即使你没看过福尔摩斯也该看过柯南吧)。

    也有一种观点认为,编程和调试是一回事,编程的过程就是逐步调试直到获得期望的结果为止。你应该总是从一个能正确运行的小规模程序开始,每做一步小的改动就立刻进行调试,这样的好处是总有一个正确的程序做参考:如果正确就继续编程,如果不正确,那么一定是刚才的小改动出了问题。例如,Linux操作系统包含了成千上万行代码,但它也不是一开始就规划好了内存管理、设备管理、文件系统、网络等等大的模块,一开始它仅仅是Linus Torvalds用来琢磨Intel 80386芯片而写的小程序。据Larry Greenfield 说,“Linus的早期工程之一是编写一个交替打印AAAA和BBBB的程序,这玩意儿后来进化成了Linux。”(引自The Linux User’s Guide Beta1版)在后面的章节中会给出更多关于调试和编程实践的建议。