什么是bug
bug原意是“臭虫”,现可用来指代计算机上存在的漏洞,原因是系统安全策略上存在的缺陷,有攻击者能够在未授权的情况下访问的危害。广义上,bug可用作形容各领域范围内出现的漏洞或缺陷。-摘自百度
bug翻译过来就是虫子的意思,那为啥会用来指代计算机的漏洞呢?
第一台计算机的bug
1946 年,霍普发现了第一个电脑上的 bug。
这是计算机第一个bug,也是最大的一个bug。
在 Mark II 计算机上工作时,电脑不能正常运作了,霍普和整个团队都搞不清楚为什么。
后来才发现,是一只飞蛾意外飞入了一台电脑内部而引起的故障。终于把问题解除了,霍普在日记本中记录下了这一事件。
这是第一台计算机出现漏洞时发现的虫子,也是第一个bug,因为这只虫子导致计算机停止了工作。
这便是bug的由来!
博主bug郭的由来
为啥我会叫bug郭,因为博主经常写几行代码,就有一页报错,有时候比较幸运,编译没有错误,运行却出现bug,所以我就是bug附体了!
调试什么
往往事情都是有迹可循,不可能天衣无缝,如果顺着迹象往下便是犯罪,往上顺藤摸瓜便是破案,找出真相!
简单的说调试就是找bug的过程!
就如同警察办案一样,逐渐将真相揭露找到凶手的过程就是调试!
调试的重要性
编程入门者百分之80的时间在写代码,百分之20的时间调试。
而大佬写百分之20时间写代码,百分之80的时间调试。
我写几行代码便需要找半个小时的bug,这样说我有当大佬的潜质!
调试的重要性!
调试步骤
发现bug
你要知道你的程序有bug
定位
大概知道,bug出现的代码区域位置。
找到bug
经过你的一番查找,然后找出bug把找到!
改bug
你需要指定一个方案,将出现bug的代码修改。
重新运行
运行更改后的代码,发现是否成功!
学会这5步,让你不在迷行调试。让bug无处躲藏!
Debug和Release区别
在调试之前不得不介绍一下Debug和Release区别
可以看到我们VS配置管理器下面有两个版本,Debug和Release这两个版本有什么区别呢?
Debug
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优
的,以便用户很好地使用。
代码:
#include<errno.h> #include<stdlib.h> int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (NULL == pf) { perror("fopen"); return -1; } //读文件,随机读写 // //关闭文件 fclose(pf); pf = NULL; return 0; }
可以看到一个程序在不同的版本下运行后,生成的程序文件大小不同,Release发布版本明显小于Debug调试版本。
因为Release版本,将代码进行了优化,并且不能调试。
我们的调试都要在Debug版本下才能进行。
Release的优化
博主,你说优化就优化了啊,拿出证据!
上代码:
#include<stdio.h> int main() { int i = 0; int arr[10] = { 0 }; for (i = 0; i <= 12; i++) { arr[i] = i; } return 0; }
Debug报错
Release运行成功!
我们一起分析一下这个代码,这个代码明显有问题,为啥在Release下编译过去了呢?
这是因为Release进行的优化,变量的储存位置也不一样。
这个代码的具体问题会在调试案例中具体讲到!
调试快捷键
F5:启动调试,经常用来直接调到下一个断点处。
F9:创建断点和取消断点 断点的重要作用,可以在程序的任意位置设置断点。这样就可以使得程序在
想要的位置随意停止执行,进而一步一步调试下去。
Ctrl+F5:直接执行不调试,如果想让程序运行起来不调试。
F10:逐过程,通常是一条语句或者一个函数,不会进入函数体内部。
F11:逐语句,一条语句,一条语句执行,会进入函数体内部执行。
这些是VS下面最基础的一些调试快捷键,通常这些快捷键配和使用,效果更佳!
调试窗口
执行调试的时候才能看到调试窗口!
在调试,进行后该如何发现问题,找到bug呢?
调试窗口,是我们破案的工具,是我们的第三只眼睛!
通过调试窗口,你可以观察代码的走向,是否和你预期的值一样。
调试的时候观察变量信息
查看变量的值
这里有3个窗口,监视,自动窗口,局部变量,及时。
监视: 可以通过输入变量,一直观察变量的值
自动,局部窗口: 随着代码调试的执行不同时刻不同变量的值会出现。
通常我们常用的是监视窗口,一直监视变量的值,看它随着调试的逐步执行,是否和我们预期的一样!
内存窗口
你可以通过内存窗口,输入你想观察变量的地址查看,它的地址和储存。
&input便出现了input的地址和存储。还有很多窗口,我就不一一介绍了,自己多多尝试,多多进步!
发现bug
编译出错
这是最简单的一步,当你的代码报了警告,错误,如果编译不过去这边是bug。
而这种是最低级的bug,只需要,根据错误,将指定代码行更改即可!
运行出错
运行错误,是最令人头疼的,当你的代码,经过一番更改,没有错误,警告,运行后出现了控制台,你准备欢呼的时候,程序突然崩了,或者结果并不是自己预期的结果,显然很让人头疼。
这时候不要慌,问题不大,bug郭带你调试!
定位bug
找到bug出现的大概位置。
如何找到bug具体出现的位置呢?
养成写代码的好习惯,写一点编一点,不要一顿操作写了几千行代码,然后运行一下,啪,出现一页bug这时整个人都麻了。
如果我们写一点,编一点,就可以将问题定位到,具体的代码块!
你可以通过F9设置断点,辅助F5跳转执行到断点处。
将光标移动到你要创建断定的位置行,按住快捷键F5创建了断定,通过F5执行到断点处,再通过,F10或者F11执行下去!
找到bug并更改bug
在通过你敏锐的调试侦查后,你便找到了bug。bug相信你一定可以的!
调试案例练习
案例 一:
#include <stdio.h> int main() { int i = 0; int arr[10] = {1,2,3,4,5,6,7,8,9,10}; for(i=0; i<=12; i++) { arr[i] = 0; printf("hehe\n"); } return 0; }
这个代码居然死循环了,为啥会发生这样的事情,这时就得上调试,干他!
当i=12时
奇怪的事情发生了,i居然变成了0。
估计这个bug得研究一整天!
不过bug郭带你研究!
首先为啥程序不会奔溃呢?数组都越界访问了?
因为程序在死循环中来不及奔溃!
为啥突然i就变为0了?
当程序执行到arr[12]=0时,arr[12]和i在同一块空间,所以i的值变为了0。
案例二:
求阶层1!+2!+…+n!的和
int main() { int i = 0; int sum = 0;//保存最终结果 int n = 0; int ret = 1;//保存n的阶乘 scanf("%d", &n); for(i=1; i<=n; i++) { int j = 0; for(j=1; j<=i; j++) { ret *= j; } sum += ret; } printf("%d\n", sum); return 0; }
但我们输入3时,预期结果:
1+2+6=9;
but运行却是15
你可以调试一下,发现问题出在哪里。
如果你代码量够,不用调试便可以发现问题,但是这是一个调试案例,自己调试一下!
如何避免Bug
为啥我们要调试找bug呢,就不能避免bug吗?
优秀的代码:
代码运行正常
bug很少
效率高
可读性高
可维护性高
注释清晰
文档齐全
让我们看看vs函数库中的优秀代码如何实现的
示范:
模拟实现库函数:strcpy char * strcpy(char * dst, const char * src) { char * cp = dst; assert(dst && src); while( *cp++ = *src++ ) { } return( dst ); }
看后是不是不由自主的赞叹一句,秒
常见的coding技巧:
使用assert
尽量使用const
养成良好的编码风格
添加必要的注释
避免编码的陷阱。
assert
断言,通常用来,判断expression是否为FALSE(假)
如果是便程序停止。
而代码库里代码assert(dst && src);
便是判断dst和src是否为空指针。
空程序便报错,精准定位bug
const
我们之前已经知道const关键字修饰变量,该变量便具有了常属性,无法被更改。
const在*的右边,该指针变量无法被改变
char * const src说明src(指针)无法被更改。
const在*的右边,该指针指向的变量无法被改变
const char * src说明*src(变量)无法被更改
学到了吗?
我们来试试模拟实现一下strlen函数
#include <stdio.h> int my_strlen(const char *str) { int count = 0; assert(str != NULL); while(*str)//判断字符串是否结束 { count++; str++; } return count; } int main() { const char* p = "abcdef"; //测试 int len = my_strlen(p); printf("len = %d\n", len); return 0; }
是不是很perfect!
今天的调试小技巧就学到这里,下去多动手调试调试!
博主在这里祝愿大佬们写的代码没有bug,最好的祝福,送给兄弟们!