目录
一、bug的由来
二、调试
调试是什么呢
调试(debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。
调试的步骤
- 发现程序错误的存在
- 以隔离、消除等方式对错误进行定位
- 确定错误产生的原因
- 提出纠正错误的解决办法
- 对程序错误予以改正,重新测试。
Debug和Release
Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release通常称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
通常我们所说的调试,就是在debug版本的环境中,找代码中潜伏的问题的一个过程。
Release版本是不能进行调试的。
三、Windows环境调试介绍
1.调试环境的准备
选择“debug”选项。
2.常用快捷键
F5 启动调试,经常用来直接跳到下一个断点处。
F9 创建断点,取消断点。
断点的作用:可以在程序的任意位置设置断点,使程序在想要的位置随意停止执行,继而一步 步执行下去。
F10 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者一条语句。
F11 逐语句,每次都执行一条语句,可以进入调用函数的内部,看函数内部执行的具体过程。
Ctrl+F5 开始执行,不调试。
结合实际操作例图理解:
(1)开始执行,不调试
(2)具体调试过程
3.调试时如何查看程序当前信息
(1)查看临时变量的值
监视窗口——在调试开始之后,用于观察变量的值。
(2)查看内存信息
4.查看调用堆栈
5.查看汇编信息
四、实例
//求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,应该输出9,但实际输出是15,为什么呢?
五、如何写出优秀的代码?
优秀的代码:
- 代码运行正常
- bug很少
- 效率高
- 可读性高
- 可维护性高
- 注释清晰
- 文档齐全
常见的coding技巧:
- 使用assert(断言)
- 尽量使用const
- 养成良好的编码风格
- 添加必要的注释
- 避免编码的陷阱
六、assert和const的作用
通过例题“写一个函数,实现strcpy”展现
//写一个函数,实现strcpy
char* my_strcpy(char* dst, char* src)
{
char* cp = dst;
while (*cp++ = *src++)
;
return (dst);
}
int main()
{
char arr[] = { "xxxxxxxx" };//8个x
char ch[] = { "hello" };
my_strcpy(arr, ch);
printf("%s\n", arr);
}
注:为什么输出是“hello”而不是“helloxxx”呢?因为printf函数,遇到\0就会停止打印,把ch copy到arr的时候,\0也会被copy过去。这也与while循环的条件判断部分也有关系,此处先使用后++,将“0”赋给第五个“x”后,进行++,\0被赋给第六个“x”,\0对应的ASCII的值为0,即为假,停止循环。(注意的点是\0已经被赋给第六个“x”)
assert的作用
如果arr或者ch为空指针会怎么样呢?
没有输出,编译器也不显示错误。这非常不利于我们寻找错误的原因。
但是如果我们加上断言,就能得到相应的提示。(assert判断的是真和假)
const的作用
运行结果告诉我们,断言失败,在第70行,知道了bug所在的位置,便于我们进行修改(特别是在一些大型程序中)。
加了const,这个错误被很好的防止。
如何更好的理解const呢?请看下图。