点击跳转指点位置
什么是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
你需要指定一个方案,将出现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
,最好的祝福,送给兄弟们!