目录
一、什么是bug?
二、调试
1.一般调试的步骤
2.Debug 和 Release
三、调试环境准备
四、调试时要查看的信息
1.查看临时变量的值
2.查看内存信息
3.查看调用堆栈
4.查看反汇编信息
5.查看寄存器
五、练习
六、常见的coding技巧
七、const的作用
八、编程常见的错误
我们平时会口头说 bug ,报错,waring(报警)等,bug 英文的意思是虫子,然而在计算机发展史上的第一只 Bug ,真的是因为一只飞蛾意外走入一电脑而引致故障,因此Bug从原意为臭虫引申为程序错误。
这个时候就需要我们的调试 来开启新大陆
关于程序错误的 参考资料
平时敲代码,总会遇到与一些问题导致程序执行不过去,你可能在那一直盯着刚写完的代码看(心里想这到底哪里出错了,但是就是没有找打错误的原因),这时就需要我们平时了解到的调试来解决问题(起先使用可能不熟练,慢慢来)
调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
接下来调试下方代码
#include
int main()
{
char* p = "hello word!";
printf("%s\n",p);
return 0;
}
在debug版本下 (执行程序)文件名.exe 是几十KB
而在release版本下 是 几 KB(原因是代码大小和运行速度上都是最优的)
再看下方代码
#include
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i <= 12;i++)
{
arr[i] = 0;
printf("haha\n");
}
return 0;
}
该程序的【执行结果】 无限循环打印 haha
而在release版本下
没有死循环 打印了13行的haha
二者区别是因为:变量在内存中开辟的顺序发生了变化,影响到了程序执行的结果
如果要对代码进行调试首先要准备好调试的环境
就是要在debug版本下,才能使代码正常调试
(点击开始调试)或者按F5
在这里介绍一些调试的快捷键
其他快捷键
在按调试后,观察变量的值
例如 输入 i
一直按F11当 i 的值变为 11时 i值的变化(0-11)
在内存窗口 输入 &i(找到i 的内存地址)
反映的是调用逻辑
【例 1】
//实现代码:求 1!+2!+3! ...+ 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;
}
输入 1,输入2 和我们预想的结果一样,但当我们输入 3 的时候结果应该是 9 实际输出结果为:
打印的结果出错了
接着进行调试,当调试到 i= 2是 正常的
调试到 j = 3 是 ret 应该是 6 ,但是发现 ret由4 变到 12
经果分析我们发现 原来是ret 每次进入内层的for循环 ret 的值接着上次的执行结果继续算
这时 我们在内层for循环上方加上 ret =1;
//实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出
#include
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;
ret = 1;//添加的代码
for (j = 1; j <= i; j++)
{
ret *= j;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
【例 2 】死循环的原因
#include
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i <= 12;i++)
{
arr[i] = 0;
printf("haha\n");
}
return 0;
}
调试后发现
【例】模拟实现库函数strcpy、
库函数strcpy
//模拟实现strcpy
#include
#include
char* my_strcpy(char *des,const char *src)
{
assert(des != NULL);
assert(src != NULL);//避免字符串为空
char* temp = des;
while (*des)
{
*des = *src;
des++;
src++;
}
return (temp);
}
int main()
{
char* str = "ab";
char arr[20] = "xxxxxxxxxx";
printf("%s\n",my_strcpy(arr,str));
return 0;
}
优化
#include
#include
char* my_strcpy(char* des,const char *src)
{
assert(des != NULL);
assert(src != NULL);
char* temp = des;//用于返回首元素地址
while (*temp++ = *src++)
;
return des;
}
int main()
{
char *arr1 = "abcdef";
char* arr2[20] = {0};
printf("%s\n",my_strcpy(arr2,arr1));
return 0;
}
const 在 * 左边
int num =0;
int n = 0;
const int *p =#
p = &n; //ok
*p = 20; //error
const 在 * 右边
int n = 1000;
int num = 0;
int * const p = # //限制了指针变量本身
p = &n; //error
*p = 20;//ok
【小总结】
const 修饰指针变量的时候:
练习:模拟实现strlen
//模拟实现strlen
#include
#include
int my_strlen(const char* str)
{
assert(str != NULL);
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char* str = "abcdefg";
printf("%d\n",my_strlen(str));
return 0;
}
编译型错误
直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不
存在或者拼写错误
借助调试,逐步定位问题。