关于调试

         其实我一直想写一篇文章关于调试的(起码在做大一实训TA的时候就有这想法),但是一直未写,原因有二:第一,我不懂怎么组织语言,第二,我懂的调试方法不多,怕写出来被人鄙视。但是经过这个星期的停车场事件,这根导火线啊,让我郁闷不已,很多问题都是调试可以解决了,起码到目前为止我遇到的都是,所以最终决定写了,虽然我不懂组织语言,但我只需要把最基础的、把思想表达就好,虽然我懂的调试方法不多,但足以应对现阶段师妹师弟们遇到的困难。

         首先当然要吐槽一下,以后师弟师妹们别一来就弹一句:“豆腐能不能帮我调试一下?”之类的话,并不是所有的师兄都像我这么好人的,再且,你们问人的技巧也应该提高一点,不要一来就把整个project发给我,然后说里面有错误,帮我找找,或者说哪个函数有问题,我更乐意的是你们能写一个测试样例,这个测试样例会产生你刚才说的错误,起码这能在一定程度上减少我的负担,有时候当你写了测试样例,我可以在一分钟内找出错误,这不是夸大。举个例子,有个师妹曾这样问我:

首先她直接发了两张图说:

     

然后就问我这是什么问题,稍微写过c++的都知道导致这类问题的原因很多,你什么都不发给我就让我诊断出什么问题,当我是神了。

         正题了,在这里我介绍三种调试方法,其实思想都差不多的(太久没用C++,写得不好见谅,毕竟用惯java,遇到错误会直接提示哪一行发生错误)。

方法一:单步调试(要Debug而不是release,方法:点击菜单栏生成->配置管理器,然后在你的项目后把配置选择为Debug)。先讲讲一些快捷键,

F5:运行到断点或运行到要用户输入的地方就会停止下来。

F10:就是单步调试,按照当前的程序一行一行执行,把代码中每一行都看成简单语句(遇到函数不会跳进去,直接一步运行到位,该函数里面有断点)

F11:功能跟F10差不多,只是在遇到函数的时候会进入该函数进行调试。

Shift + F5:结束本次调试。

Ctrl + F10:直接运行到光标所在的行,除非过程中遇到断点。

Shift + F11:跳出当前函数(一步把当前调试行所在的函数运行完),除非过程中遇到断点。

 

断点的设置,把光标定位于要设置断点的行,按一下F9,如果不对断点进行任何的修改,则默认遇到断点就会停止执行,但是断点是可以设置条件的,

    

就是当满足你设置的条件的时候程序才会停止执行。

 

此外在调试的过程中可以通过监视窗来看你想知道的变量的值


当你设置了你要监视的变量后,假如当这个变量的值发生改变,则在监视窗口中变量值的颜色会变成红色以突出(比如上面的变量c)。

 

 

方法二:利用断言。这需要用到头文件assert.h里面的assert(expr)函数,这个函数会判断表达式expr是否为真,如果expr为假,则程序会终止并输入他运行到哪个文件哪一行。

#include <assert.h>

 

int main() {

       int a = 10;

       assert(a ==8);

       return 0;

}

比如这个程序,很明显表达式a == 8为false,则控制台上会有这样的输出信息:

Assertion failed: a == 8, filee:\workspace\c++\test\test\test.cpp, line 5

然后程序会终止执行。

 

 

方法三,利用输出信息。就比如在关键的地方打印一下信息。比如:

#include <iostream>

 

int main() {

       int a = 10;

       std::cout<< a << std::endl;

       return 0;

}

 

我想知道程序运行过程中a的值是多少可以加上这条输出信息,从而判断是否正确。又比如:

#include <iostream>

 

void fun() {

       std::cout<< "fun()" <<std::endl;

}

 

int main() {

       fun();

       return 0;

}

想知道fun()函数有没有被调用,则可以通过这样输出信息的方法来判别。

 

 

         看完上面的基础介绍,也许你会说,这些我都懂啊,可是就是调试半天也找不出错误,别急,慢慢来。假如是写C++控制台的程序,我一般都是方法一加方法三,先通过方法三来快速定位大概的错误位置,然后再用方法一来精确定位错误的位置,但是现在都只用方法三来调试了。举个例子:

比如以下程序:

#include <iostream>

#include <stack>

using namespace std;

 

stack<int> s;

 

void fun(){

       cout <<"***for test: " << "fun()" << " ***" << endl;

       s.pop();

}

 

void fun1(){

       cout <<"***for test: " << "fun1()" << " ***" << endl;

       cout <<s.top();

}

 

int main() {

       int a = 1;

       int b = 2;

       a = b + 1;

       s.push(a);

       fun();

       a++;

       fun1();

       cout <<a;

       cout <<endl;

}

运行过程中发现出现以下错误:


也许你就会很惊讶,不知道哪里有问题,实际上很多人问我都是出现这类问题,这时候你可以注意观察控制台的输出信息,可以看到有输出信息:

***for test: fun() ***

***for test: fun1() ***

这可以说明在调用fun1()函数之前都没问题(至少没有非逻辑错误),于是可以进一步定位这错误是在调用fun1()函数后发生的,于是可以从fun1()开始进行调试,通过这样一次一次逼近,错误的范围就可以收敛了。

         也许你定位到是s.top()这句发生问题,对于学过C++的人来说都知道发生这样的错误只能是栈为空,这时候你就在需要查找一下对s在哪里进行了操作(怎么查找很简单,就是通过ctrl+F来查找),如果存在多处操作的话,你可以对每个操作设置一个断点(假如你很清楚你程序的执行过程可以省略这步),然后通过不断使用F5来看对s的操作顺序,这样就可以很快查出错误。比如这个例子,你就可以定位到是由于前面有一个出栈的操作导致栈变为空。其他的情况就类似了。

         假如是存在逻辑错误,那可能会比较麻烦,因为程序不会出现运行错误,很难定位在哪里会出现问题,对于这种,我建议是先在一些关键的位置是使用断言或者打印信息。

比如这个例子:

#include <iostream>

using namespace std;

 

int main() {

       int a, b = 1;

       a = b++;

       cout <<a << endl;

       return 0;

}

你期望输出a的值是2,但实际上是1,这样你就可以知道在打印信息之前发生了逻辑错误,然后再继续往上看,也是通过这样的方法来定位错误的。

更一般的,你就单步调试,通过不断监视变量值的变化来找错(通过监视窗口)。

Ps:一般是你自己写的程序,你都应该能够大概知道是在哪里发生错误,这样就能从一开始把范围缩到很小。就正如我刚开始说的那样,我更喜欢你们写一个测试样例会产生你说的错误,因为这样能帮我很快定位哪里有问题,继续往上推敲。

我本人是很喜欢程序出错有异常的,我debug速度如此快就是因为我对上面讲得方法很熟悉,运用得很熟练(特别是方法三,因为我现在写程序能用的工具就只有方法三),所以说下次遇到问题可以先别找人,自己尝试debug。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值