编程必备小技巧——调试

前言

大家好,今天给大家带来的是VS的实用调试技巧

这是我们学习编程一定要学会的

如果大家看过我的上一篇关于扫雷的博客(在最底下的专栏目录中),并且大家有自己去写过扫雷,可能在写的过程中会遇到各种挫折,为了解决程序的问题,我们就会用到今天的关于调试的知识

好了,废话不多说,开始我们今天的一个分享吧

1.什么是bug

bug本意的是虫子,但现在一般是指在电脑系统或程序中,隐藏着的⼀些未被发现的缺陷或问题,简称程序漏洞

关于bug这个词语的由来,大家感兴趣的话,可以点击下面的这个链接去自行了解哦

历史上的第一个计算机Bug

2.什么是调试

不知道大家在平时写程序时有没有出现过以下的这个情况

b9fe153d30f14ad386a29632c7f5f8ef.png

当我们发现程序出了问题后,可能有很多同学(包括现在的我)都在循环上面的这张图

其实这就引出了调试的概念:

调试描述的是一个找问题的过程,英文叫debug

上面的图片其实就是一种调试,但这是一种不科学的方式,想要知道bug到底是怎么写出来的,为此,我们就要学会科学的调试方法

下面是调试的一般过程:

1.首先是承认出现了问题

2.然后通过各种手段去定位问题的位置,可能是逐过程的调试,也可能是隔离和屏蔽代码的方式,找到问题所在的位置

3.然后确定错误产生的原因

4.再修复代码, 重新测试

说到这,可能大家已经按耐不住想去写代码了,但大家先别急

在开始正式的调试之前,我们再来了解一个概念:Debug 和 Release

3.Debug和release

那什么是Debug和Release呢?

打开VS,我们在界面上会看到下面图片上圈起来的

这就是 Debug 和 Release

Debug通常称为调试版本,程序可以调试,并且不作任何优化,便于程序员开发

Release为发布版本,这是给用户的版本,此时的版本已经做了各种优化,但程序不能调试

所以我们在开始调试之前,一定要先设置成Debug模式才可以

如果是Release则没有调试功能!

4.VS调试快捷键

下面我们来介绍几个快捷键,方便各位进行调试的操作

1.F9 : 创建断点和取消

2.F5

3.F10

4.F11

5.CTRL+F5:这个快捷键我们很熟,我们至今写过了所有代码运行都按这个快捷键

它的意思是 开始执行不调试,如果我们想让程序直接运行而不调试就可以使用

(现在你知道为什么按CTRL+F5了吧)

首先我们来看F9 什么是断点?

这个红色的点就是断点

其作用是可以在程序的任意位置设置断点,打上断点就可以使得程序执行到想要的位置暂定执行,接下来我们就可以使用F10,F11这些快捷键,观察代码的执行细节

然后我们看一下F5,它一般是和F9配合使用,作用是启动调试,经常用来直接跳到下一个断点处

大家看,我们启动调试后按下F5,它会从第一行直接跳到断点所在位置,跳过前面所有的代码

这可以让我们随时调试我们想调试的部分,非常的便捷

最后我们再来了解两个快捷键:F10和F11

F10:逐过程,通常来处理一个过程,一个过程可以是一次函数调用,或者是一条语句

F11:逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部

在函数调用的地方,想进入函数观察细节,必须使用F11,如果使用F10,直接完成函数调用

 可以看出,虽然两者都是逐过程的调试,但F11的调试力度要更细

关于断点,我们再来讲一个东西:条件断点

请看下面这张图

当我们将条件设置为i==8是,再次调试,我们看一下监视窗口

我们只调试了一次,因为条件断点的原因,i直接跳过了01234567,来到了8,因为我们条件设置的就是i==8,这就是条件断点很巧妙的一个作用

5.监视和内存观察

在调试的过程中我们,如果要观察代码执行过程中,上下文环境中的变量的值,有哪些方法呢?

那我们就来说说监视和内存观察

1.监视观察

关于监视观察,我们在上面讲条件断点的时候就用到了

打开监视窗口的步骤如下:

启动调试

然后打开监视窗口(4个窗口随便点开一个就行,这里用监视1举例)

添加要监视的项并观察

2.内存观察

如果监视窗口看的不够仔细,也是可以观察变量在内存中的存储情况

步骤如下

我们可以看到这里可以输入一个地址

还是那上面讲监视观察的代码举例,如果我们想看arr的地址,那我们就在这个地址:输入arr

让我们看一下结果

这个时候地址会自动转换成arr的地址

我们再来试一下num(对于变量,我们输入是要在前面加上&

注意:关于地址中的内容是什么意思涉及进制转换的知识,这些内容会在以后的博客中分享,大家可以订阅博客最底下的专栏持续的关注我哦

除此之外,在调试的窗口中还有:自动窗口,局部变量,反汇编、寄存器等窗口,大家下去可以自己用一下,玩一下。这里不再过多赘述了

6.调试举例

1.例子1

请看下面的代码,运行的结果是什么呢?

在x86环境下(不是x64),打印的结果是死循环的 hehe

我们可能只发现数组越界,但结果的死循环是不是意料之外呢?

所以这时候如果不调试你压根就不知道为什么

我们来打开监视窗口看一看吧

我们发现,当i大于10时,数组不仅“明目张胆”的越界,而且此后还将arr[ 10 ] 擅自赋值为0

当i = 11 后也将arr[ 11 ]赋为0

而当我们将arr[12]也赋为0后,我们发现i也变为了0!

其实我们细心一点,就会发现此前的arr[12]就一直与i保持同步

我们再将它们俩的地址取出了看一下

我们发现它们的地址是一致的  这样也就解释得通死循环的原因了

不过为什么又会出现这种情况呢?是偶然还是必然?

下面我们再来分析一下:

在我们上面写的这个代码中,i和arr都是局部变量

我们知道,局部变量在内存中是存在栈区的

栈区内存的使用习惯是从高地址向低地址使用的,而数组在内存中的存放是:随着下标的增长,地址是由低到高变化的

如图

所以上述代码中的变量i的地址是较大(高)的。(因为我们先定义了i再定义arr)arr数组的地址整体是小(低)于i的地址

所以根据这些,就能理解为什么了

如果是上面的内存布局,那随着数组下标的增长,往后越界就有可能覆盖到i,这样就可能造成死循环的(那上图来看就是i和arr 数组之间恰好空出来2个整型的空间)

这里肯定有同学有疑问:

为什么i和arr 数组之间恰好空出来2个整型的空间 呢?

注意:这里确实是一个巧合事件,可能在不同的编译器下中间的空出的空间大小是不一样的, 所以 这个题目是和环境相关的(这里的环境就是VS2022 Debug模式 x86)

虽然是巧合,但通过这个例子我们也确实感受到了调试有多么重要了!

2.例子2

上面的代码其实代码量是比较小的

但如果代码稍微复杂一点呢?

我们又该怎么调试呢?

这里我们就上手调试一下我们上一篇博客写的扫雷的代码

假设我在写扫雷的代码时遇到了下面这种情况:

在我的扫雷中,初始化棋盘没问题,打印棋盘也没问题,但我发现布置雷有问题

这个时候怎么办呢?

首先我们在SetMine函数内部打断点,快速跳转到函数SetMine

然后我们打开监视窗口进行调试观察

调试是需要反复去动手练习的

要多敲代码,千万不要因为觉得太简单而不敲,敲与不敲是两回事,要有空杯心态!

7.编程常见错误归纳

好了,重头的关于调试举例的分析说完了,接下来的内容就很轻松了

其实下面的第7点是一个拓展包

我们在这里对编程常见的错误进行一个归纳,我们写代码出现的错误基本上都可以归为以下三类

1.编译型错误

编译型错误⼀般都是语法错误,这类错误一般看错误信息就能知道为什么了,

编译型错误,随着对语言的熟练掌握,会越来越少,也容易解决

2.链接型错误

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在

一般是因为 :

1.标识符名不存在 

2.拼写错误 (比如字母大小写没对上)

3.头头件没包含或者引用的库不存在

3.运行时错误

运行时错误,一般是我们想要的结果与程序运行的结果不同

这种错误的形式是千变万化的,也正是需要借助我们今天讲的调试来逐步定位问题,

结语

好了,今天的分享就到这里了

最后,希望大家点个赞或者关注吧(感谢感谢)

让我们在接下来的时间里一起成长,一起进步吧!

f9f30f716e7a4d259882d2294937b207.jpg

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃肉的Humble

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值