一步步使用Code::Blocks进行设置断点调试程序

一、调试之前要做的工作

首先,我们要确保Code::Blocks的配置正确,调试工作才能进行得更顺利

为此,我们需要生成调试符号。调试符号可以让调试器知道代码的哪一行正在执行,这样你就可以知道程序运行到哪里了。

为确保调试符号设置正确,请在Code::Blocks中选择项目 | 编译选项(Project | Build Options),会看到这样一个对话框:

你需要确保调试(Debug)目标里的生成调试符号(Produce debugging symbols)选项被勾选上。

还需要在编译 | 选择目标 | 调试(Build | Select Target |Debug)中,确保调试(Debug)作为项目的目标被选中:

以上操作确保了目标是对项目进行调试,调试器将使用调试符号来编译你的程序。

二、设置断点

调试器的价值在于,它能让我们看到程序正在做的事情——哪些代码正在执行,以及变量的值是多少。

为此,我们在程序的某个地方设置断点,然后在调试器下运行该程序。调试器将执行程序,直到到达设置了断点的代码行。此时,编译器便可以让你查看程序,或者一步步地执行程序,检查代码的每一行是如何影响你的变量的。

首先我们来看一段用来计算特定数额资金的利率(interest rate)、年利息(compounded annually)的程序:

#include <iostream>
using namespace std;
double computeInterest (double base_val, double rate, int years)
{
    double final_multiplier;
    for ( int i = 0; i < years; i++ )
    {
        final_multiplier *= (1 + rate);
    }
    return base_val * final_multiplier;
}

int main ()
{
    double base_val;
    double rate;
    int years;
    cout << "Enter a base value: ";
    cin >> base_val;
    cout << "Enter an interest rate: ";
    cin >> rate;
    cout << "Enter the number of years to compound: ";
    cin >> years;

    cout << "After " << years << " you will have " << computeInterest( base_val, rate, years ) << " money" << endl;
}

 看了运行结果,很明显,出现了错误,这时我们将设置断点开始进行调试。

1,先在main函数开始的地方,设置一个断点。这样就可以查看整个程序的执行过程了。

(1)将光标移到 double base_val 这一行

(2)选择 调试 | 设置断点 (Debug | Toggle Breakpoint)或者按下F5。这会在该代码行旁边的侧边栏中设置一个小红点,表明这一行有一个断点:

(3)可以使用设置断点命令或者单击小红点用来设置或取消设置该断点。

(4)开始运行程序。选择调试 | 开始(Debug | Start)或者按下F8。

这样程序将正常执行,直到遇到断点。

现在我们应该看到了打开的调试器,它看起来应该是这样的:

首先要注意的是小圆点下面的三角形,它表示接下来要执行的代码行。它跟小红点之间相隔若干行。

它之所以没有紧挨着小红点,是因为变量的声明不产生任何的机器代码,因此,尽管断点看起来是在15行,但实际上它在第18行。

(5)这时应该还有一个监视(Watches)窗口打开了,如下图:

我已经展开了监视窗口的两个子项:局部变量(Local variables)和函数参数(Function Arguments)。

监视窗口会显示出所有当前可用的变量,包括局部变量和函数参数,以及这些变量的值。

注意:这里看起来像乱码的原因是因为我们还没有对它们进行初始化,这也是接下来的几行程序所要做的事情。

(6)为了执行接下来的几行代码,我们需要告诉调试器向下执行下一行(F7)

所谓向下执行一行,就会执行当前的代码行,也就是三角形所标识的那一行。

一旦走到下一行,程序就会执行cout语句,输出一条信息到屏幕中,要求你输入一个值。

如果你尝试输入一个值但没有任何效果——因为程序还在调试器的控制之下。

再次按下F7后,程序会等待用户输入,因为这时候cin函数还未返回——cin函数需要在返回前得到用户的输入。

重复这一过程,分别输入0.1给利率,输入1给年数。

现在,断点到达了这一行代码:

再次确认输入是否正确。我们可以通过监视窗口来检查局部变量的值:

注意:rate的值不是0.1,是因为0.10000...1中最末尾的1只是浮点数的一种怪异的表达方式(浮点数并不是精确的),它实在太小了,对大多数程序来说不会造成很大影响。

(7)现在我们确定一切都没问题,来调查一下computerInterest函数中会发生什么,单步执行(Step into)

 单步执行会进入当前行的函数里面去执行,而不像下一行命令,只是执行函数然后显示给你最终的结果。

现在我们就单步进入computerInterest函数之中:

(8)从结果中我们可以看出函数的参数部分一切正常,但变量i 和 final_multiplier 值不对劲。

为此,使用下一行命令(F7),执行循环语句,由于它与一些初始化操作相关联,我们看看会发生什么。

(9)从中我们可以看出,final_multiplier没有正确初始化。而且,接下来要执行的语句将要用到final_multiplier:

    final_multiplier *= (1 + rate);

这条语句的意思是,将final_multiplier乘以(1+rate),再把结果重新赋值给final_multiplier。但是我们看到final_multiplier并没有被初始化,因此这个乘法的结果也将会是一个莫名其妙的值。

(10)如何修复bug?

我们需要在声明final_multiplier变量的语句中,把它也初始化。在这个例子中,它应该被初始化1。

double final_multiplier=1;

(11)修复bug后的运行结果为:

三、总结

通过以上一个简单的程序案例,使我掌握了调试一个程序的基本流程,和分析bug的过程,为以后自己独立寻找bug,解决bug提供了实用的技能。

我不光可以写bug,还能Debug!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Aloys的开发之路

Linux快捷键

Shell 快捷键 <Ctrl k>:删除从光标到行尾的部分 <Ctrl u>:删除从光标到行首的部分 <Alt d>:删除从光标到当前...

2459
来自专栏美团技术团队

Node.js Stream - 实战篇

背景 前面两篇(基础篇和进阶篇)主要介绍流的基本用法和原理,本篇从应用的角度,介绍如何使用管道进行程序设计,主要内容包括: 管道的概念 Browserify的...

3715
来自专栏技术专栏

慕课网Flask高级编程实战-2.搜索书籍路由编写

将上一小节的判断语句全都写到search函数中的几个缺点: 1.使得代码非常的臃肿,现在只有两个判断就占用了6行,如果有多个判断,search方法就要爆炸了 ...

2983
来自专栏玄魂工作室

PHP代码审计Day2 - filter_var函数缺陷

--------------------------------------------------------------------------------...

1192
来自专栏养码场

Java后端开发面大集锦1.0,汇集了各大公司的面试点!你都能答上来吗?

各位在面试时,必然会遇到各位的技术问题。这次针对Java后端开发,以下这篇文章罗列了各大公司技术面试官可能会提及的问题,并做出了解答。若觉得不错,希望分享给更多...

613
来自专栏Coco的专栏

【基础进阶】URL详解与URL编码

1889
来自专栏Golang语言社区

Golang Template 简明笔记

作者:人世间 链接:https://www.jianshu.com/p/05671bab2357 來源:简书 前后端分离的Restful架构大行其道,传统的模板...

8816
来自专栏逸鹏说道

Python3 与 C# 并发编程之~ 进程实战篇

之前说过 Queue:在 Process之间使用没问题,用到 Pool,就使用 Manager().xxx, Value和 Array,就不太一样了:

1634
来自专栏jeremy的技术点滴

ansible简易入门之playbook

3064
来自专栏JAVA高级架构开发

一个分布式java爬虫框架JLiteSpider

A lite distributed Java spider framework. 这是一个轻量级的分布式java爬虫框架

5690

扫码关注云+社区

领取腾讯云代金券