【C语言】代码的调试技巧

Hello everbody!时隔一周的时间,咱们又见面喽!最近一直在忙操作系统的事情,没能抽出时间,还请见谅呀。

今天,我打算给大家讲讲有关调试的知识。有些宝子是非科班,可能对调试还是挺陌生的。等c语言学习过后到了数据结构,咱们需要编写的程序的代码量明显增加。单是初阶数据结构的顺序表,链表,代码量就轻轻松松超过一百行。如果我们写出了BUG,迟迟找不到出现问题的原因,这将是一件令人十分抓狂的事情。有些时候,咱们碰碰运气,这儿改改,那儿改改。咦!程序居然该对了!这时我们可能十分开心,觉得之前努力敲的代码总算没有白费,可是回头一想:刚才的程序到底错在哪里呢?

到了数据结构部分,如果不会调试,可以说是寸步难行。因此,我认为给大家讲一讲调试是十分有必要的,这可以大大提升我们的学习效率。

【C语言】代码的调试技巧_第1张图片

1.学习工具

建议大家使用Visual Studio进行学习。因为它的调试功能是比较强大的,而且它与标准C的契合度比较高。不太建议使用dev和vc6.0。dev的调试功能不太行,与标准C的契合度不如Visual Studio,长期使用会给大家带偏的,而且它没有中文版本,给我们的学习带来了比较大的阻碍。vc6.0的版本比较老了,而且已经很久没有更新了,也不太建议使用。

下面我将用Visual Studio2022版给大家讲讲调试技巧。

2.小问题考考大家

【C语言】代码的调试技巧_第2张图片

咱们先看一看这个小程序,但凡有学过C语言的宝子们都可以看出来这个程序的问题:就是越界了呗!arr数组明明只有十个数据,你却循环了13次,这肯定是脑子进水了的写法!咱们把for循环中的13改成10,那程序肯定就正常啦!是的,你说的都对!但是我想问的是:这个程序为什么是死循环呢?如果你去公司应聘,面试官给你一个类似的程序,问你该程序可能输出的结果,你能答上来嘛?当然,如果知道原因的小伙伴可以把答案写在评论区呦!

想要知道原因,咱们就必须调试!逐字逐句的分析代码的运行情况!

到本文结尾,我会给大家讲讲死循环的原因!

3.Debug和Release的介绍

【C语言】代码的调试技巧_第3张图片

Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序,找出程序的错误。

Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以使用户很好的使用。换句话说,公司研发的成品,用户拿到的就是发布版本。

【C语言】代码的调试技巧_第4张图片

看,还是同样的代码,换成Release版本就不再是死循环了。可见Release对先前的版本确实进行了优化。

【C语言】代码的调试技巧_第5张图片

【C语言】代码的调试技巧_第6张图片

当然,我们同样可以通过对比Debug版本的可执行程序和Release版本的可执行程序的大小也可以看出,Release版本的可执行程序要小得多。

4.学会快捷键

1.CTRL+F5,这个大家应该是最熟悉的。作用就是让我们写好的程序跑起来。

2.F9,创建断点和取消断点。断点的作用:可以在程序的任意位置设置断点。这样就可以使得程序在想要的位置随意停止执行,继而一步一步执行下去。当然也可以手动设置断点。

【C语言】代码的调试技巧_第7张图片

如图,在代码的最左边边用鼠标点一下即可。当然,我们也可以设置调试断点:

3.F5,启动调试,经常用来直接跳到下一个断点处。很显然,这个快捷键是要和断点联合使用的!

4.F10,逐过程,一个过程可以是一个函数调用,也可以是一条语句。

5.F11,逐语句,就是每次只执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最常用的)。

5.调试找出死循环的原因

下面我将用上文死循环的例子用调试功能检查一下死循环的原因!

首先我们需要主观判断一下程序出错的大致位置,由于这是一个死循环程序,那么大概率是循环体出错了。所以我们把断点的位置设置在循环体处。(左边的小红点就是断点)

【C语言】代码的调试技巧_第8张图片

断点设置好后再使用快捷键F5开始调试。

【C语言】代码的调试技巧_第9张图片

开始调试后我们可以看到程序执行到第14行就自动停了下来,前面的test函数已经执行过了,hehe已经打印到屏幕上。如果宝子们觉得这样观察得还不够细致,也可以这样:

【C语言】代码的调试技巧_第10张图片

点击调试按钮,找到窗口,窗口里面有一个监视选项,监视中的四个选项都是一样的,随便点击一个即可。

【C语言】代码的调试技巧_第11张图片

这是打开监视后的界面,我们可以输入任何我们想观察的变量。这个程序是死循环,那就表明i总是小于13的,我们就可以观察一下i的变化。当然,如果你想看得更加清楚,arr数组也可以观察一下!

【C语言】代码的调试技巧_第12张图片

输入i和arr后,我们可以十分清晰的观察到目前i的值为1,arr数组中首元素已被赋值为1。

这时我们可以用F10或是F11让程序一步一步执行下去,并密切关注i和arr数组的变化情况:

【C语言】代码的调试技巧_第13张图片

目前开始执行第三次循环,目前一切正常,没有出现什么问题,那么我们就可以继续调试下去,直到出现问题为止。

【C语言】代码的调试技巧_第14张图片

i的值已经来到了9,说明循环已经跑了十次,目前程序仍然是正常的。说明问题出在数组越界的部分,但是到后面arr数组就观察不到了怎么办呢?

其实调试功能是非常强大的,我们依然可以观察arr[10],arr[11],arr[12]。只需输入到监视窗口中即可!

【C语言】代码的调试技巧_第15张图片

我们把arr[10],arr[11],arr[12]输入到监视窗口后,由于循环只跑了十次,arr[10],arr[11]还没有被赋值,编译器给到的是随机值。但是arr[12]好像格外的与众不同,它的值是9,i的值也是9!这难道是巧和嘛?目前还不能十分确定具体的原因,那么我们继续按F11让程序走起来。

【C语言】代码的调试技巧_第16张图片

我们进入第11次循环的内部,此时arr[11]的值被该成了1(看来这个程序的胆子还是很大的,都越界了还可以改)。但这并不是我们观察的焦点,我们更应该把注意力放在i和arr[12]的变化情况。

我们可以看到:进入第11次循环后,由于自增,i的值变成了10。但是神奇的是arr[12]的值也变成了10!这就不正常了,按理说arr[12]的值不该变呀,还没轮到它变呢!

这时,相信聪明的宝子们已经发现该程序死循环的原因啦!当然为了观察得更清楚,我们依然可以继续调试下去。

【C语言】代码的调试技巧_第17张图片

调试到这里,i得值增加到了12,arr[12]也变成了12,那么下一步,就要对arr[12]赋值啦,当arr[12]的值变成一后,i会怎么变呢?是也变成1,还是不变呢?

【C语言】代码的调试技巧_第18张图片

arr[12]赋值为1后,i不出意外的也变成了1。那么死循环的原因就呼之欲出啦!i的值最大就是12,永远不会超过12,这就是死循环的原因。但是为什么i的值和arr[12]的值一致,并且时时刻刻跟随着它变化呢?

难道它们两个是同一块空间嘛?如果是同一块空间的话,这一切就解释的通了。当然,这也只是我们的猜测。我们同样可以通过调试来验证我们的猜想!

【C语言】代码的调试技巧_第19张图片

通过观察它们的地址,我们发现,它们的地址居然是一样的!既然地址一样,那么它们就是同一块空间,它们的值同时变化也就不奇怪了!

6.死循环的底层逻辑

我们已经用调试功能找出了死循环的原因,那么为什么arr[12]的空间刚好和i的空间一致呢?真的那么巧合嘛?

其实这取决于局部变量空间的创建逻辑。

像i,arr都属于局部变量。局部变量都创建在栈区,而栈区内存的使用习惯是:从高地址向低地址使用。而数组随着下标的增长,地址是由低到高变化的。

【C语言】代码的调试技巧_第20张图片

再次仔细观察我们的代码会发现:i是先声明的,而arr是后声明的,那么i的地址就必然比arr的地址高,至于高多少就取决于编译器了,像刚才我们用的vs2022,i的地址就刚好比arr地址高了3个整型空间。当然如果是vs2019可能只高两个整型空间。

【C语言】代码的调试技巧_第21张图片

大家也可以验证一下自己的编译器是什么情况!

那换句话说,如果我们先声明arr,再声明i。那么arr的地址就必定比i高。不管数组再怎么越界与i也没有任何关系,就不会出现死循环啦!

【C语言】代码的调试技巧_第22张图片

7.结语

当然,仅仅学会这些快捷键是远远不够的。一个人的调试水平不是一蹴而就,而是日积月累,调试能力逐渐提高的。今后在公司面试时,你的调试能力也是面试官重点关注的呦!因此在面试时,咱们写出了错误的代码不要紧张,一个程序员写出BUG再正常不过啦!运用咱们深厚的调试功底,在短时间内找出BUG并改正同样可以让面试官眼前一亮。所以,加油吧各位宝子们!

你可能感兴趣的:(c语言,开发语言)