在学习了一段时间编程语言后,我们已经具备使用代码来解决部分问题了,但在面对某些问题是,我们的代码符合语法规定,但实现的功能与我们设计时所设想的内容似乎存在差异或是一些小细节上处理的方式存在问题,这时我们往往会说是系统存在bug,那究竟什么是bug?处理bug的方式又有些什么?且让我们细细道来。
一,什么是bug?
在电脑系统或程序中,隐藏着一些未被发现的缺陷或问题,简称程序漏洞,也就是我们现如今所说的bug。
二,什么是debug?
在我们发现程序存在漏洞(bug)时,我们便会想办法去去找到bug产生的原因,并消除问题,这个发现问题并修复问题的过程被称之为调试,也就是debug(消灭bug的意思)。
三,debug和release
在VS编译器上有Debug与Release两个选项,Debug我们知道了,那么Release是什么意思呢?
原来这是两个版本的意思,Debug相当于开发者版本,而Release相当于用户版本。
Debug通常被称为调试版本,它包含调试信息,且不做任何优化,用于程序员在开发时对程序进行调试,在选择Debug版本后编译产生的可执行文件包含调试信息,是可以直接进行调试的。
Release被称为发布版本,它往往进行了各种优化,使得程序在代码大小与运行速度上都是最优的,便于用户很好的使用。程序员写完代码后可将版本调整为Release版本,该版本不包括调试信息。
对比两图我们便能直观的发现二者在内存上明显的差距。
四,VS调试快捷键
调试最常使⽤的⼏个快捷键:
F9:创建断点和取消断点
断点的作⽤是可以在程序的任意位置设置断点,打上断点就可以使得程序执⾏到想要的位置暂定执
⾏,接下来我们就可以使⽤F10,F11这些快捷键,观察代码的执⾏细节。
条件断点:满⾜这个条件,才触发断点
F5:启动调试,经常⽤来直接跳
到下⼀个断点处,⼀般是 和F9配合使⽤。
F10:逐过程,通常⽤来处理⼀个过程,⼀个过程可以是⼀次函数调⽤,或者是⼀条语句。
F11:逐语句,就是每次都执⾏⼀条语句,但是这个快捷键可以使我们的执⾏逻辑进⼊函数内部。在函
数调⽤的地⽅,想进⼊函数观察细节,必须使⽤F11,如果使⽤F10,直接完成函数调⽤。
CTRL + F5:开始执⾏不调试,如果你想让程序直接运⾏起来⽽不调试就可以直接使⽤。
VS更多快捷键了解:http://blog.youkuaiyun.com/mrlisky/article/details/72622009
五,调试和内存观察
在调试的过程中我们,如果要观察代码执⾏过程中,上下⽂环境中的变量的值,有哪些⽅法呢?
这些观察的前提条件⼀定是开始调试后观察,⽐如:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int num = 100;
char c = 'w';
int i = 0;
for (i = 0; i < 10; i++)
{
arr[i] = i;
}
return 0;
}
1.监视
开始调试后,在菜单栏中【调试】->【窗⼝】->【监视】,打开任意⼀个监视窗口,输⼊想要观察的对象就行。
打开监视窗⼝:
在监视窗口观察
F10逐个过程进行:
2.内存
如果监视窗⼝看的不够仔细,也是可以观察变量在内存中的存储情况,还是在【调试】->【窗口】->
【内存】
打开内存窗口:
在内存窗口中观察数据
六,调试举例
了解了基本的知识要点之后,让我们来看看实践案例。
下面是一串有趣的代码:
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i <= 12; i++)
{
arr[i] = 0;
printf("hehe\n");
}
return 0;
}
代码并没有发生我们想象的堆栈溢出现象,而是它运行起来居然死循环了?
这究竟是为什么?让我们调试起来看看:
我们发现变量i的胆子居然这么大,居然无视for循环的规定都增加到13的值了!
这究竟又是怎么一回事?
调试该程序的内存布局图如下:
1. 栈区内存的使⽤习惯是从⾼地址向低地址使⽤的,所以变量i的地址是较⼤的。arr数组的地址整体是小于i的地址。
2. 数组在内存中的存放是:随着下标的增⻓,地址是由低到⾼变化的。所以根据代码,就能理解为什么是左边的代码布局了。如果是左边的内存布局,那随着数组下标的增⻓,往后越界就有可能覆盖到i,这样就可能造成死循环的。
这⾥肯定有同学有疑问:为什么i和arr数组之间恰好空出来2个整型的空间呢?这⾥确实是巧合,在不同的编译器下可能中间的空出的空间⼤⼩是不⼀样的,代码中这些变量内存的分配和地址分配是编译器指定的,所以的不同的编译器之间就有差异了。所以这个题⽬是和环境相关的。
调试举例:函数
如果⼀个代码稍微复杂,那怎么调试呢?
这⾥我们就上⼿调试⼀下函数的代码。
演⽰:
• 在函数内部打断点,快速跳转到函数
• 在数组传参,调试进⼊函数,如何在监视窗⼝观察数组的内容: 数组名,n 的形式
调试过程中,要做到⼼中有数,也就是程序员⾃⼰⼼⾥要清晰的知道希望代码怎么执⾏,然后再去看
代码有没有按照我们预定的路线在执⾏。
调试是需要反复去动⼿练习的,调试是可以增加程序员对代码的理解和掌控的,掌握了调试的能⼒,
就能看到本质,就像能给程序做B超⼀样,对程序内部⼀览⽆余。
七,编程常见错误归类
编译型错误
编译型错误⼀般都是语法错误,这类错误⼀般看错误信息就能找到⼀些蛛丝马迹的,双击错误信息也能初步的跳转到代码错误的地⽅或者附近。编译错误,随着语⾔的熟练掌握,会越来越少,也容易解决。
链接性错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。⼀般是因为
• 标识符名不存在
• 拼写错误
• 头⽂件没包含
• 引⽤的库不存在
运行时错误