第1章 C/C++调试基本知识
1.1 BUG与Debug
什么是BUG呢?BUG的本意是虫子,现在泛指计算机硬件或者软件中的错误、缺陷等。我们随处可以听到BUG这个词:电梯运行不稳定,就会有人说“电梯出BUG了”;手机App功能不正常,也会有人说“App出BUG”了。这种表达方式没错,但是虫子为什么会成为软硬件错误的代名词呢?关于BUG一词的来源,还有一个小小的传说。
这个传说与计算机科学家Grace Hopper有关。
Grace Hopper是著名的计算机科学家,而且是第一位获得美国海军少将军衔的女性。1947年9月9日,作为程序员的Grace Hopper发现Mark II计算机出现了故障。经过一番排查之后,她发现引起计算机故障的原因是一只死了的飞蛾卡在了计算机的某个电器元件中。当把这只飞蛾取出来后,故障也就解决了。Grace Hopper当时把这件事记录了下来,并且把那只飞蛾粘贴在当天的工作手册中,并写下了“First actual case of bug being found.”的字句,如图1-1所示。
图1-1 BUG来源的传说
从此这个故事就广为流传,后来BUG一词就用来指代计算机硬件或者软件中的错误以及缺陷等。
那么什么是Debug呢?我们都知道,在英文中De是前缀,具有“去除”和“离开”的意思,比如detach(分离),因此Debug就是去除BUG的意思。但是怎样去除BUG呢?过程就是Debug,我们一般不将Debug称作除错,而是叫作调试,因为反复的调试过程才能去除BUG。调试过程很复杂,要修改代码、借助工具进行测试等,有时候甚至比开发一个软件还要复杂。
很多人可能都会有这种感觉,即我们开发一个小功能可能只需要1个小时,但是去除其中的错误可能会花费一天甚至更长的时间。事实表明,我们在发现问题、解决问题的过程中往往需要更多的智慧与技巧。
加拿大著名的计算机科学家Brian Wilson Kernighan说过一句有趣的名言:“调试代码的难度是编写代码的两倍,如果编写代码的时候已经黔驴技穷,那你便没有足够的聪明才智去调试它了。”原文如图1-2所示。
图1-2 Brian Wilson Kernighan有关调试的言论
这句话看似简单,实则对调试充满敬畏。的确,虽然编写和调试代码看起来密不可分,但是确实都需要特别的能力。无论是编写代码还是调试代码,都需要发挥我们的聪明才智。
调试的形式多种多样,只要是为软件去除BUG的过程或者行为,甚至有时候不一定是去除BUG的过程或者行为(比如优化),都可以被称为软件调试。在我们的印象中,好像只有在调试器中运行软件才叫作调试。其实调试有很多种方式,比如我们可以在调试器中运行软件进行调试,也可以分析软件的转储文件去进行调试,等等。我们可以将软件调试定义为“发现和去除BUG的过程或者行为”。
要解决BUG,首先要定位BUG的根源(root cause),然后为BUG提出解决方案。定位BUG根源的过程往往要比提出解决方案困难很多,一旦找到了问题根源,就会有各种方案来解决问题。
有一个小故事很好地诠释了定位BUG和为BUG提出解决方案这两者之间的关系。
20世纪初,美国福特公司正处于高速发展时期,多个车间和厂房被迅速建成并投入使用,客户的订单堆满了福特公司销售处的办公室。福特汽车供不应求。就在这时,福特公司的一台电机出了问题,这几乎导致整个车间不能运转,相关的生产工作也被迫停了下来。公司调来大批检修工人反复检修,又请来许多专家进行检查,却始终没有找到问题的根源,更谈不上维修了。这时有人提议去请著名的电机专家Charles Proteus Steinmetz(如图1-3所示)来帮忙。
图1-3 Charles Proteus Steinmetz
Steinmetz 仔细检查了电机,然后用粉笔在电机外壳画了一条线,对工作人员说:“打开电机,将记号处里面的线圈少绕16圈。”令人惊异的是,工作人员照办后,故障竟然排除了,福特公司很快就恢复了生产。
福特公司经理问Steinmetz要多少酬金,Steinmetz说:“不多,只需要1万美元。”“1万美元?就只简简单单画了一条线!”Steinmetz看大家迷惑不解,转身开了个清单:画一条线,1美元;知道在哪儿画线,9999美元。福特公司经理看了之后,不仅照价付酬,还重金聘用了Steinmetz。
这个故事用在这里非常合适,不仅体现了发现问题的根源比提出解决方案更重要,而且说明了找到问题的根源是一种技能,也更有价值。我们调试的目的通常是找到“画线”的地方,这就是调试的重要性。