编码入门加速器-分享两个Debug的技巧
作者:老九—技术大黍
社交:csdn
公众号:老九学堂(新手有福利)
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
前言:
对于C/C++初学者而言, 出现bug是一件非常头疼的事情, 但如果掌握了技巧, 找bug就是一件很容易的事情了.
其实,我们程序员就是一群调bug的人而已。没有开发过的人肯定不会同意我这句话,但是,我相信这句话对职业老鸟们来说,一定不会反对。
今天我们用C语言和Visual Studio 2019来介绍两个Debug的方法 调试的基本用法, 二分法查找bug, 以及比较经典的小黄鸭查找法.
调试:
逐步执行定位问题 是找bug最好的方式, 那么这里就出现了两个重点 逐步(逐行执行)与定位(每个值的变化).
相信很多初学者 在代码遇到bug时, 都会习惯用[打印语句]来手动实现调试.
比如可能绕晕过小伙伴的冒泡排序:
小白 : 大佬帮我看看哪里有问题
for (int j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j + 1] = arr[j];
arr[j + 1] = temp;
}
}
张三:加上打印语句试一试就知道了!
..3分钟后
for (int j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
printf("0: %d,%d,%d\n", arr[j], arr[j + 1], temp);
temp = arr[j];
printf("1: %d,%d,%d\n", arr[j], arr[j + 1], temp);
arr[j + 1] = arr[j];
printf("2: %d,%d,%d\n", arr[j], arr[j + 1], temp);
arr[j + 1] = temp;
printf("3: %d,%d,%d\n", arr[j], arr[j + 1], temp);
}
}
"emmmmmm, 似乎是1和2周围出现的问题."
"找到了! 需要将arr[j + 1] = arr[j]; 修改成arr[j] = arr[j + 1];"
这种方式, 虽然看起来很好用. 但手动加上打印语句实在太麻烦了, 而且加上打印后 代码可读性变得很差.
另外, 由于不能中间暂停(加上scanf后可读性更差了), 输出测试会被前后的打印语句所影响, 输出也不好看.
我们代码圈有一句经典名言: "不会偷懒的程序员不是好程序员".
于是, 就要请出我们的主角: 集逐步与定位于一身的调试功能.
在开发中, 我们会经常采用调试来跟踪代码运行. 代码运行过程中出现bug时, 启用调试可以快速找到bug出现的位置, 以及在运行时变量值的变化.
举例:
在这里我们用VS2019举例(由于篇幅限制, 本章就跳过其他编译器的调试说明了, 小伙伴如果有需要可以自行百度)
首先, 在需要调试的代码行左边点击一下(打上断点), 表示程序运行到此处时暂停
接着, 启动调试器.
现在我们就进入了VS2019的调试界面
在这里, 我们可以手动控制程序的运行速度, 并且每一步 都可以在监视区看到每个变量的状态.
于是, 我们就可以很快的看到.
1:
点击下一步
2: 这里temp值变红 表示temp的值有了变化
点击下一步
3: 很明显 这里arr[j+1]的值也被改变了.
按照正常的交换流程, 接下来就是给arr[j]赋值为tem......
等下! temp的值 好像上一步就被改变成10了
bug找到了! 在第2步时, arr[j+1]的值是9, arr[j]的值是10,temp的值也是10.
按顺序应该是将arr[j+1]的值赋值给arr[i]才对!
小结:
使用调试功能, 只需要在可能出现bug的代码前打上断点, 即可开始逐步调试. 相比使用打印语句来找bug, 不仅省事了很多, 而且界面也看起来高大上!
学会调试前:
学会调试后:
我们学会了吗?
二分法:
二分法, 我们通常也叫折半法.
是一种将区间逐渐变小, 直到查找到指定位置的方法.
首先, 我们在预估有bug的代码块中间打上断点(注意 这里中间是指运行过程的中间, 而不是代码行的中间)
如果值在前面就出现了异常, 说明是前半段逻辑的问题
如果值没有出现异常, 说明是后半段逻辑出现了问题
接着在前半段/后半段代码块的中间打上断点 重复以上步骤即可
举例:
这里我们随机抽取一个小伙伴的作业
题目:
某个小伙伴的代码:
然后悄悄的改一点, 使其出现bug
死循环了amazing!
接下来 我们就要开始调试查找了
在循环的末尾打上断点, 并运行
第一遍
half的值是candy每个元素/2 candy的每个元素值都是[i] = half[i] + half[i+1].
(这里是举例教大家如何调试, 所以这段代码不需要深究. 日常调试中能看出来值是否有问题就可以)
运行正确
点击这个按钮, 表示跳到下一个断点. 由于我们只有一个断点 所以会跳到下次循环的这一行
第二遍:
candy的值出现问题了所以可以判断 是第二次循环开始 值出现了异常
现在我们将断点打在程序的中间, 然后启动调试, 直接进入第二次循环
上半部分的代码是将数组平分, 所以按理candy和half的值应该是相同的才对.
上半部分的代码出现了问题
接着我们在上半部分的代码中打上断点 逐步调试 .
由于上半部分的代码是单个循环, 里面放了判断分支, 所以直接在循环处打上断点
同样跳到第二次循环, 开始调试,
第一遍进入了if部分, 值正确.
看来是else部分出现的问题
由于下次循环就进入else了, 这里可以不用再打断点重新调试.
马上就要接近bug的真相了!
进入else后, 元素的值并没有相等
问题就在else的两次赋值中出现了
现在我们看看else的代码
candy[i]先自己/=2 然后给half[i]赋值时再/2. 于是导致了half[i]接收到的值 实际上是candy/4的值.
解决方案就很简单了, 只要先candy给half赋值 再自除就好了
搞定!
修改代码后, 运行一下试试
解决了!
总结:
现在我们总结一下找到bug的流程
最开始 我们通过在循环逻辑处打断点的方式, 先找到了程序运行第X次循环时出现的问题
接着, 我们在第X次循环的中间打上断点 发现是上半部分出现的问题
然后, 在上半部分的代码中调试, 发现是else部分的bug
最后, 查看else部分的两行代码, 发现了问题
大家学会了吗?
小黄鸭调试法(补充)
某天, 张三在食堂看到同学李四愁眉苦脸, 于是问"你怎么了?",
"唉, 我遇到了一个bug. 我的代码需要实现balabala, 于是 我想着先alala, 然后再balabala, 后来在clacl... !" 李四灵光一现, 于是中断了聊天, 饭也没吃完就回去改bug了.
相信很多小伙伴都有这样的经历, 遇到bug卡住了, 在和他人描述的中途找到了解决办法.
其实这种方式有一个术语: 小黄鸭调试法.
小黄鸭调试法是软件工程中使用的调试代码方法之一。就是在程序的调试、纠错或测试过程中,耐心地向小黄鸭解释每一行程序的作用,以此来激发灵感。
举例:
需要准备的内容:
1.一只小黄鸭(或者长得像小黄鸭的任意物品):
出来吧皮卡丘, 就决定是你了!
2.一段有代码的bug
如果你不希望让别人看到:
找bug找了很久的同学 似乎入魔到想教会手中的玩偶写代码.jpg
这样的奇景. 你还需要一个安静的空间.
接下来就可以开始对着小黄鸭进行介绍每段代码的功能了:
接着愉快的找到了bug
以上就是一些找bug的常见技巧, 大家学会了吗?
代码bug出现的方式千奇百怪. 相应的, 找bug的技巧也多种多样. 大家平时在学习中有哪些好用的小技巧, 也可以放在评论区大家一起交流, 讨论.
最后
感觉有用的同学,请记得给大黍❤️关注+点赞+收藏+评论+转发❤️
作者:老九学堂—技术大黍
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。