当前位置:C++技术网 > 精选软件 > 新手应该掌握的调试程序的基本流程

新手应该掌握的调试程序的基本流程

更新时间:2017-04-20 22:18:54浏览次数:1+次

    调试不是简单的F5和F10。调试也是一门艺术,如何高效的调试,也是区别高手和菜鸟的非常有效的招数。我说一下我调试的过程,供新手参考。
    调试,首先是定位错误。那么定位错误可以是浅层的单步调试跟踪,还可以是深度的多维度的测试调试。什么意思呢?一开始,我们先对目标代码单步执行一遍,重现错误。找到错误位置,可以先简单分析一下出错位置的代码,搞清楚这部分代码的作用,以及这些代码之前衔接的代码,比如传入的参数之类的。如果当时停在了错误的位置,还处于调试状态,那么我们就要看看相关的变量的值,来寻找蛛丝马迹。很多时候,指针错误有指针为空、指针悬空(指针没有有效的内存指向)、错误指向(指向不该指向的变量造成逻辑混乱)等等。其中指针为空、指针悬空编译器都会进行明显的崩溃提示。这种错误很好定位初步的位置。至少是能够找到在哪崩溃的。当然,崩溃的位置不代表就是错误出现的位置。因为前面一个错误的处理,可以导致后面N多层之后的错误。而中间的各层还是正确的。这样的错误,虽然找到崩溃点,但是不一定真的找到问题的位置。
    所以说,第一步是找到崩溃或者发生错误的位置。这是寻找问题的一个入口。如果问题比较简单,通常就可以直接分析出错位置的代码找到问题。如果问题复杂,那就还要继续。
    对于复杂的问题,我们可能需要反复的执行多次调试,在多次重现的过程中,可能会找到出错的规律。出错的规律有,数据的规律的变化,内存地址的规律变化,还有条件的规律触发或者到达一定次数才崩溃。所以,多次尝试调试运行,可以收集到不少错误的规律。很多逻辑问题,可以通过这个方法解决。
    当然,再复杂的问题,就是能够重现,但是你就是找不到什么规律,每次就一样的现象。不着急,你已经找到问题的入口,慢慢深入就行了。因为程序的逻辑是一个流程的流水线方式。逻辑的先后顺序是程序的根本。我们在一个地方出现错误,很多时候并不一定是出错位置的问题。前面的结果不对,后面导致了崩溃,是很常见的。比如说前面获取一个数据,然后后面一个地方进行除法,数据是不确定的,所以你也没有预测到一些特例。然后用户输入了0,结果后面也没有对0进行检查,直接除了,就发现除零错误。发现了除零错误,算你幸运,这个错误编译器会提示。假如逻辑上出错,你就很难觉察了。所以这也是需要跟踪前面代码的流程的必要性了。
    程序的流程是调试的最重要的依据,如果你对流程不清晰,是很难调试找到问题的。调试的人一定要在心里有一个流程图,每一步都做了什么,每一步应该达到什么效果。当然,你在调试时可以用单步执行的方式,一步步从前面往后走,直到问题出现。因为构成程序流程的不仅仅是局部的代码,还有多个函数的调用,甚至还有全局变量的参与,这些都让调试变得麻烦。跨度越大,调试越困难。这样也就需要对程序的流程做到胸有成竹。我们通过出现错误的位置,根据不合理的参数,一步步往前的跟踪,在单步调试时寻找变量变化的过程,一旦出现数据异常,就可以停下来了。然后仔细分析相关的变量和局部的逻辑,试图寻找问题的根本原因。
    然后很多时候,程序里代码很复杂,也导致你跟踪起来困难重重,以至于从到尾跟踪一遍,都找不到问题在哪里。那么到这里浅层的单步调试跟踪就不能解决问题了。
    那么我们需要使用深度的多维度的测试调试。因为实际的代码是复杂的,各个部分都是相互关联的。而且复杂的数据结构都会让我们眼花缭乱。那么我们就需要进行单元调试测试。我们通过临时注释一些功能逻辑,用排除法来检测错误。如果注释了一部分代码,问题同样出现,那么注释的代码就不是产生错误必须的。就这样一步步的注释,甚至是增加一些临时代码,确保逻辑正确执行,但又可以保证引进的代码不构成错误。之前我遇到一个坑,最后我注释掉我所有的功能代码,然后使用单独的测试代码,结果测出了问题。如果只是不停的研究自己的代码,怎么也找不到问题。越是复杂的问题,越是返璞归真,用最基础的代码去测试。当然,这是基于大量测试得到的、现象和特征,然后写对应的简单的测试代码测试其他部分的代码是否正常。
    调试找问题,绝不是简单的单步运行那么简单。调试手段可以层出不穷,只有大量的实践你才能掌握。调试也是一件很有意思的事情,当然也要有强大的心理素质。
    以上的方法,描述了调试的一般的过程,但不是全部。问题总是很多的。有一个经验,越是难找到问题的,很可能是低级错误,以至于让你都忽略了他的存在。而复杂的错误,看上去气势恢宏,实则不堪一击。而对于怎么看都看不出来的问题,也有可能是你的知识有限,所以就是问题摆在你的面前,你也看不懂问题出在哪里。