目录
前言:作为程序员,写bug那简直是家常便饭,轻则几个重则几十个,你还在人脑查bug吗?学习调试,恐怕能给你带来不少的帮助以及对代码运行的理解。
一、什么是bug
我们常说bug,那么bug又是什么呢?bug本意是“昆虫”或“虫”,现在一般是指在电脑系统或程序中,隐藏着的⼀些未被发现的缺陷或问题,简称程序漏洞。
“Bug”的创始人格蕾丝·赫柏(Grace Murray Hopper),她是一位为美国海军工作的电脑专家,1947年9月9日,格蕾丝·赫柏对Harvard MarkII设置好17000个继电器进行编程后,技术⼈员正在进行整机运行时,它突然停止了工作。于是他们爬上去找原因,发现这台巨大的计算机内部⼀组继电器的触点之间有一只飞蛾,这显然是由于飞蛾受光和热的吸引,飞到了触点上,然后被高电压击死。所以在报告中,赫柏用胶条贴上飞蛾,并把“bug”来表示“⼀个在电脑程序里的错误”,“Bug”这个说法⼀直沿用到今天。(引自:程序错误_百度百科 (baidu.com))
二、什么是调试(debug)
当我们发现程序中存在的问题的时候,那下一步就是找到问题,并修复问题。这个找问题的过程叫称为调试,英文叫debug(消灭bug)的意思。
在进行调试的时候,我们要做好这样的觉悟,就是承认自己的代码存在问题。如此,我们才能心平气和的去修复问题,否则只会越改越气急败坏,特别是人脑查bug,这是对内心的一大考验,解决问题之前一定要承认问题的存在。
三、vs2019的两种编程环境
“ Debug”通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序;程序员在写代码的时候,需要经常性的调试代码,就将这里设置为 debug ,这样编译产生的是debug 版本的可执行程序,其中包含调试信息,是可以直接调试的。
“Release”称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。当程序员写完代码,测试再对程序进行测试,直到程序的质量符合交付给用户使用的标准,这个时候就会设置为 release ,编译产生的就是 release 版本的可执行程序,这个版本是用户使用的,无需包含调试信息等。
对比可以看到从同一段代码,编译生成的可执行文件的大小,release版本明显要小,而debug版本明显大。
我们想要进行调试,当然应该在debug环境下进行。
四、调试常用快捷键
F9:创建断点和取消断点。断点的作用是可以在程序的任意位置设置断点,打上断点就可以使得程序执行到想要的位置暂定执行,接下来我们就可以使用F10,F11这些快捷键,观察代码的执行细节。
F5:启动调试,经常用来直接跳到下一个断点处,一般是和F9配合使用。(若不打上断点,程序就不会停下知道运行结束或出错终止)
F10:逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
F11:逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部。
在函数调用的地方,想进入函数观察细节,必须使用F11,如果使用F10,直接完成函数调用。
CTRL+F5:开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。
VS更多快捷键了解见此博客:VS中常用的快捷键_vs快捷键-CSDN博客
五、监视和内存观察
我们已经知道如何通过调试快捷键来进行调试进程了,那么我们该如何观察程序中的变量以及程序对于内存空间的使用呢?
这里我们将使用监视窗口与内存窗口来进行观察(一定是在调试状态下才能进行观察):
监视窗口:
内存窗口:
六、调试举例
#include <stdio.h>
int main()
{
int n = 0;
int i = 1;
int ret = 1;
int sum = 0;
for (n = 1; n <= 10; n++)
{
for (i = 1; i <= n; i++)
{
ret *= i;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
运行结果:
10阶乘求和得到数值还是比较大的,难以判断是否正确,我们可以稍加修改:
//n阶乘求和
#include <stdio.h>
int main()
{
int n = 0;
int i = 1;
int ret = 1;
int sum = 0;
for (n = 1; n <= 3; n++)
{
for (i = 1; i <= n; i++)
{
ret *= i;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
运行结果:
我们知道3的阶乘求和为9,很明显代码出现了问题,接下来我们进行调试:
打上断点,直接来到循环部分,因为变量的初始化一般没有什么问题。接着打开监视窗口,观察关键变量的值的变化。
通过分析,发现,原来在每次计算阶乘时,要给ret初始化,而这里没有造成了错误。则将代码改为:
//n阶乘求和
#include <stdio.h>
int main()
{
int n = 0;
int i = 1;
int sum = 0;
for (n = 1; n <= 3; n++)
{
int ret = 1;
for (i = 1; i <=n; i++)
{
ret *= i;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
运行结果:
到此,bug就得到解决了。
调试是有很多方法和技巧的,每个人的方法技巧都不同,要自己慢慢体会和总结,但是调试基本操作是一样的,需要我们掌握。
七、编程常见错误归类
1.编译型错误
编译型错误⼀般都是语法错误,这类错误⼀般看错误信息就能找到⼀些蛛丝马迹的,双击错误信息也能初步的跳转到代码错误的地⽅或者附近。编译错误,随着语言的熟练掌握,会越来越少,也容易解决。其报错体现在:
2.链接型错误
⼀般是因为:标识符名不存在;拼写错误;头文件没包含;引用的库不存在。
3.运行时错误
运行时错误,是千变万化的,需要借助调试,逐步定位问题,调试解决的是运行时问题。通常因为逻辑原因或者数据调用不合理造成。