《软件调试修炼之道》Part 1(CH1~5)读书笔记 PB16110698 第八周(~4.26)

  编程中,调试几乎是必不可少的,一劳永逸、一次完成预想功能而完全不出bug的情况凤毛麟角,出现bug→调试→再出现bug→再调试……基本是软件工程中的常态。可以说,软件调试是每个coder的必修课,而《软件调试修炼之道》便是一本调试教科书,考虑到前两次大作业中出现的各种bug,我有必要系统地学习debug的一些思想和技巧,因此本周我阅读了这本书的Part 1:问题的核心。这一部分由五章组成:山重水复疑无路,重现问题,诊断,修复缺陷,反思。以下我将分别浅谈我的阅读感想。

  1、概览调试:流程、问题和原则

  第一章名为“山重水复疑无路”,颇为扑朔迷离的命名,内容却很明晰:介绍软件调试,简单概述调试的流程、原则和遇到的问题,为后四章详细铺叙起头。其中给我感触比较深的是关于调试的意义与价值的介绍。在我和身边同学的一贯认知中,调试就是为自己的bug买单,算是编程中最令人烦躁、耗费大量时间而收获寥寥、时常引起心态伴随程序一起崩的工作,我每次点下“ctrl+F7/F5”和设置断点查看结果,都伴随着难以名状的期待:但愿不要出bug。而本书中认为,真正的调试并非简单的修复缺陷,而是要发现问题、解决问题、提高代码质量并确保问题不再发生。为调试所花的时间往往比敲代码更有意义和价值。回想过去的编程经历,确实如此。我已记不清大整数加减乘除的实现算法,但却能轻易想起当初调试越界问题、数组移位进位问题的教训;编写数独程序的生成算法和QT界面代码我也印象不深了,但对QT信号传递、部件命名的debug过程还历历在目。短期而言,调试确实让我们抓耳挠腮、耗费心力却只解决了某个细节问题;但从长远来看,每一次调试都让我们交足了学费、痛定思痛,不再跌入同一个坑。同时,调试往往比重写更有效率。个人作业中的词频搜索里,由于一个判断逻辑的错误,我的输出与示例偏差很大,一度想要换算法重写。但经过调试,最终只改了一个if中的判断条件,便符合了需求,省下了不少功夫。

  原则方面,按照约定俗成的经验,我们基本上每次调试只解决一个问题,同时避免影响其他部分的正常工作,以免顾此失彼、焦头烂额。

  2、调试核心:重现、诊断、修复和反思

  重现,即发现问题,之后才能谈解决。在简单程序的debug中,往往不需要重现,因为bug就堂堂正正地摆在眼前耀武扬威;但软件发布后的调试中,很多情况下是基于用户反馈的缺陷,往往需要重现问题,来发现问题所在、寻求解决方案。书中介绍了朴素的控制变量法和玄学的推测法等。在结对编程中,我们的dll发布后交由UI组测试,与软件交付用户的流程比较相近,调试流程也基本相同。好在好心的UI组们基本都把可能的bug给我们列举出来,我们只需要用推测法简单尝试就能发现问题,也有一些问题是显而易见的,例如泛用性、便利性等。总而言之,发现问题是解决问题的第一步,重现问题,才能做到有的放矢。

  诊断,即发现问题后提出假设、进行实验,直到找到症结所在。这种思想方法在我们过往的断点debug中经常使用。当结果不符合预期时,我们会先通过断点确定问题出现的区域,再通过实验逐步找到问题语句,完成诊断。更科学地说,可以通过“插桩法”“二分法”等方式“排雷”,这些也算是老生常谈了。需要注意的是,诊断有时会造成“陷阱”,即看错症结、治标不治本。词频统计作业中,我曾在调试中改错条件,使得输出结果里原本错误的词汇统计变得正确了,但词组统计依旧错误,后来发现修改处不正确,只是生硬地打上了补丁。可见找准问题所在、外科手术式精准定位和修改后仔细筛查,是非常重要的。

  修复,是调试的最终阶段,也是最可能出问题的阶段。这是因为有些问题的症结根深蒂固,甚至修复代价大得让人难以接受。在日常生活中其实不乏这样的例子,比如某程序的核心算法存在致命缺陷,只能重构;比如某校BBS论坛的实现中纯用C语言完成,甚至连数据库都没有,落后时代十几年而基本无法有效维护、更新,修复的工作量基本和推倒重建的工作量差不多。这时,“掩盖真相”的手法应运而生:不管根本原因出在哪里,只要用一些操作来让输出结果正确就可以。这样的方法确实有一些效果,也能避免付出过大的修复代价,但高风险、留隐患的弊端也是显而易见。惭愧的是,我在结对编程中也使用了这样“掩盖真相”的手法。由于某些我debug良久而想不通的原因,生成算式中乘方(^)后跟的数字会超过我设置的限制范围,还会带上括号,最终我选择通过暴力筛选手段筛掉不合要求的式子,并通过不断超量生成式子、选出合适的式子的操作来使输出的式子都正确。客观来说,这样的做法实质上并没有解决核心问题,但实际操作中确实卓有成效,算是没有办法的办法。现实中的修复当然还是要力求解决根本问题,但有时也不得不为效率和资源做出妥协,个人而言,我真诚地期望不再使用“表面补丁”修复程序,而是更负责地进行重构。

  反思,就是吸取经验教训了。每一次调试都是为我们犯下的错误买单,包括数据结构选择的不合理、算法的缺陷、逻辑错误和键入失误等。常言道,吃一堑长一智,每次调试都是总结错误、吸取教训的宝贵经历。

 

  总而言之,调试的核心流程近似一个瀑布模型,各流程间又不断迭代。在调试中解决问题、提升程序的完整性和可靠性,正是软件调试修炼之道。

posted @ 2018-04-25 17:32  CGYR  阅读(148)  评论(1编辑  收藏  举报