一、前言
bug是啥?翻译中它是臭虫的意思
Bug一词的原意是“昆虫”或“虫子”;而在电脑系统或程序中隐藏着的一些未被发现的缺陷或问题,人们也叫它“bug”。“Bug”的创始人格蕾丝·赫柏(Grace Murray Hopper),是一位为美国海军工作的电脑专家,也是最早将人类语言融入到电脑程序的人之一。而代表电脑程序出错的“bug” 这名字,正是由赫柏所取的。1947年9月9日,赫柏对Harvard Mark II设置好17000个继电器进行编程后,技术人员正在进行整机运行时,它突然停止了工作。于是他们爬上去找原因,发现这台巨大的计算机内部一组继电器的触点之间有一只飞蛾,这显然是由于飞蛾受光和热的吸引,飞到了触点上,然后被高电压击死。所以在报告中,赫柏用胶条贴上飞蛾,并把“bug”来表示“一个在电脑程序里的错误”,“Bug”这个说法一直沿用到今天。
图片文字出自 Drake
二、我们在写代码中常见的错误
1.编译型错误
这种直接看错误提示消息(双击),解决问题,或者我们可以凭借经验就可以搞定,相对来说比较简单。例如我们一般的语法错误,句末少了’;'等。
2.链接型错误
看错误提示消息,主要在代码中找到错误消息中的标识符,然后定位问题所在。例如调用函数时函数未定义,拼写错误(函数名写错了)
3.运行时错误
这种是最隐蔽的,要借助调试逐步定位问题。
三、调试的基本知识
1.什么是调试?
找bug的过程就叫做调试
2.vs环境的两种版本
debug与release版本
版本名 | 含义 | 适用人群 | 原因 |
---|---|---|---|
debug | 调试版本 | 程序员 | 不做任何优化便于程序员调试程序(文件里面包含了调试信息) |
release | 发布版本 | 用户 | 进行了各种优化使得程序在代码大小和运行速度上是最优的(不能调试) |
三、windows系统下的调试技巧
1.调试环境的准备(环境改为debug)
2.一些快捷键的使用
快捷键 | 含义 |
---|---|
F5+F9的配合使用 | F9切换断点;F5执行完代码断点前的代码,停在断点处 |
F10 | 逐过程,不关注里面是怎么执行的,关注我们的结果(不会进入函数内部,直接得到函数结果) |
F11 | 逐语句,不放过任何细节(能进入函数内部,观察函数的实现) |
Shift+F11 | 进入函数内部后,不想继续观察函数,我们可以跳出函数,回到主函数 |
Ctrl+F5 | 不调试直接执行 |
Shift+F5 | 跳出调试 |
3.调试的时候查看程序当前信息
特别注意:下列出现的窗口信息需要我们进入调试窗口才能看到
自动窗口:自动把程序执行到这个位置的上下文环境中某些变量添加在这
局部变量:展示我们上下文环境中,就在这个局部范围内,一些局部变量的相关信息
监视:不管执行哪一句,值始终放着
内存:观察内存变换
反汇编:查看汇编代码
寄存器:我们可以观察程序执行寄存器变量
调用堆栈:可以反应函数调用逻辑
4.我们要多多动手,尝试调试
四、我们的实际演练
1.我们第一个需要调试的代码是为了各阶乘的和
说明:这个代码我们在逐语句分析的时候,发现问题出现在3的阶乘,我们再次进入调试,再逐语句分析很挫,所以我们可以在问题语句附近添加断点,设置for语句的条件,调试代码更高效
2.这个代码理解起来有难,但是这种类型的代码修改必须借助调试
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include<stdio.h>
int main()
{
int i = 0;
int arr[10]= {1,2,3,4,5,6,7,9,10};
for (i = 0; i <=12; i++)
{
printf("hehe\n");
arr[i] = 0;
}
return 0;
}
实验结果:代码是一个死循环,一直在打印hehe
想要理解这个代码,我们必须要知道:
1.栈区的默认使用规则:先使用高地址然后才使用低地址的空间
2.数组的地址是连续的,是随着下标增长而增长的
这种类型的错误因为在不同编译器它们的内存空间分配是不同的,所以我们设置的判断语句不同
vc6.0环境下<=10就死循环了
gcc编译器<=11就死循环了
vs2013 <=12就死循环了
在vs环境中i<=12它是死循环,为什么在i<=11它会报错误
因为i<=11,栈空间被破坏了
i<=12时,他不会报错,是因为12进入死循环没有机会停下代码,如果停下了也同样会报错误
很有趣的现象在debug环境下这个代码为死循环,然而我们在release版本结果就打印13个hehe
我们不妨打印他们各自的地址
这个很好的验证了我们画的那个图是正确的;并且还验证了release环境下进行了优化
那么要怎么样才能在两种版本切换的时候,出现完全不同的结果的呢?
我们的数组不能越界,越界可能导致死循环。
五、如何写出好(易于调试)的代码
a,优秀代码的7个要素
- 代码运行正常
- bug很少
- 效率高
- 可读性强
- 可维护性高(代码独立性高)
- 注释清晰
- 文档齐全
b,运用常用的coding技巧
- 使用assert
- 尽量使用const
- 养成良好的编程风格
- 添加必要的注释
- 避免编程的陷阱
补充:
assert();断言
头文件:<assert.h>
实际意义:assert函数发现问题把问题抛出来,在哪有问题,利于程序员把当前代码的问题解决掉
const的用法
例如 const int num=10;表示这个变量num是不允许更改的
注意区分指针const
const int * p//修饰的是p不能通过p来改变p的值
int * const p//修饰的是我们的p,p的值不能被更改
六、结语
欢迎大家指出不足