实用调试技巧
- 前言
- 一、什么是bug?
- 二、调试是什么?
- (一)调试
- (二)调试的基本步骤
- (三)Debug和Release的介绍。
- 1.简单介绍
- 三、Windows环境调试介绍
- (一)调试环境的准备
- (二)快捷键
- (三)调试的时候查看程序当前信息
- 1.查看临时变量的值
- 2.查看内存信息
- 3.查看调用堆栈
- 4.查看汇编信息
- 5.查看寄存器信息
- 四、一些调试的实例
- (一)求 1!+2!+3! ...+ n! ;不考虑溢出。
- (二)程序死循环的原因
- 五、如何写出好(易于调试)的代码?
- (一)优秀的代码:
- (二)示范
- (三)const作用
- (四)模拟strlen函数
- 六、编程常见的错误
- (一)编译型错误
- (二)链接型错误
- (三)运行时错误
- 总结
前言
调试相信在大多数初学者的眼里似乎不是那么重要,但是却在未来使用的时候非常重要,当你用过调试并进行深入了解后,你会停不下来用调试进行对代码的编译,所以我们大家需要好好学习调试的技巧,本章节我们将从根源剖析什么是计算机的问题以及如何去解决问题。
一、什么是bug?
bug是什么意思?bug本意是臭虫、缺陷、损坏、窃听器、小虫等意思。现在人们将在电脑系统或程序中,隐藏着的一些未被发现的缺陷或问题统称为bug(漏洞),如一些游戏中不完善的地方。 另外由于现在社会的发展bug另有一种引申意义用来形容某事物厉害的超乎想象BUG可以使电脑系统崩溃,容易被攻击,现有修复漏洞工具。
BUG的解释
史上第一个BUG,第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误。
二、调试是什么?
(一)调试
调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。
(二)调试的基本步骤
(三)Debug和Release的介绍。
1.简单介绍
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用,不能进行调试。
三、Windows环境调试介绍
我们今天演示的是VS2022 IDE编辑器下的环境。
(一)调试环境的准备
在环境中选择 Debug 选项,才能使代码正常调试。
(二)快捷键
常使用的几个快捷键:
F5 - 开始调试。经常用来直接跳到下一个断点处。不会单独使用,一般配合F9进行使用。
F9 - 创建断点和取消断点。断点的重要作用,可以在程序的任意位置设置断点。这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。设置的断点只要满足条件就跳过去,而在循环中的值不足以跳出循环,那就会在循环里面的断点一直停着,直到跳出循环到循环外的断点。
F10 - 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
F11 - 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最常用的)。
Ctrl+F5 - 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。
(三)调试的时候查看程序当前信息
1.查看临时变量的值
先F10调试,调试起来后再按照下面的步骤。
a,n,k,sum是在动态变化的。
2.查看内存信息
我们进行取地址查看一下:
3.查看调用堆栈
通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置
4.查看汇编信息
可以切换到汇编代码。
在关于函数的栈帧与创建中,有明确的书写这一部分,大家如果感兴趣可以看一看以下的链接:
函数的栈帧的创建与销毁
5.查看寄存器信息
可以查看当前运行环境的寄存器的使用信息。
四、一些调试的实例
(一)求 1!+2!+3! …+ n! ;不考虑溢出。
大家可以看看这串代码为什么输出的是15而不是9呢?
那我们来调试一下吧!
当n为3的时候,我们发现ret==2,原来是没有更新ret的值呀!在每一次的大循环的时候,ret的值都要更新为1,所以我们重新写一下代码:
//实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出
//5!=5*4*3*2*1
//
int main(){
int i = 0;
int sum = 0;//保存最终结果
int n = 0;
int ret = 1;//保存n的阶乘
scanf("%d", &n);//3
//1!+2!+3!=1+2+6=9
for (i = 1; i <= n; i++){
int j = 0;
ret = 1;
for (j = 1; j <= i; j++){
ret *= j;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
(二)程序死循环的原因
这道题目我在数组的博客中已写,大家可以转到数组的博客去看一下,也可以看我下面的图片:
【C初阶】数组中关于数组越界死循环
五、如何写出好(易于调试)的代码?
(一)优秀的代码:
- 代码运行正常
- bug很少
- 效率高
- 可读性高
- 可维护性高
- 注释清晰
- 文档齐全
常见的coding技巧:
- 使用assert
- 尽量使用const
- 养成良好的编码风格
- 添加必要的注释
- 避免编码的陷阱。
(二)示范
模拟实现库函数:strcpy。
以下博客链接是我写的关于模拟strcpy函数的博客:
模拟strcpy函数
(三)const作用
如下图,解释了const的作用。
结论:
const修饰指针变量的时候:
- const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变。
- const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
(四)模拟strlen函数
以下博客链接是我写的关于模拟strlen函数的博客:
模拟strlen函数
六、编程常见的错误
(一)编译型错误
直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。
(二)链接型错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误。
(三)运行时错误
借助调试,逐步定位问题。最难搞。
即结果是错误的,那我们就需要调试去解决这个问题。
总结
调试是很关键的,在这篇博客中,我引进了很多外部的链接,那当然都是我之前写的博客,更加详细具体,大家通过此次学习学习到了关于程序的错误和常见的调试技巧。