目录
1、调试概念:
2、Debug和Release的介绍
3、windows中的快捷键
4、案例一:求1!+ 2!+3!+...+n!
5、案例二:下面的代码输出什么?
6、案例三:实现一个strcopy的函数
7、实现一个函数strlen,用到优化:const 和assert
8、常见的错误分类 :
1、调试概念:
- 调试(英语: Debugging /Debug ),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程J
调试的基本步骤
- 发现程序错误的存在
- 以隔离、消除等方式对错误进行定位
- 确定错误产生的原因
- 提出纠正错误的解决办法
- 对程序错误予以改正,重新测试
2、Debug和Release的介绍
- Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序
- Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
3、windows中的快捷键
F9
- 创建断点和取消断点 断点的重要作用,可以在程序的任意位置设置断点。这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。
F5
- 启动调试,经常用来直接调到下一个断点处
- #F9和F5一般是配合使用的,F9设置断点,断点前的代码不做调试,要调试断点后的代码,当F10以后,断点之前的代码执行完成,在按F5会在断点之后执行一次
F10
- 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句
F11
- 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最长用的)。
CTRL+ F5
- 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。
内存上的
4、案例一:求1!+ 2!+3!+...+n!
int main()
{
int n = 0;
int i = 0;
int ret = 1;
int sum = 0;
int j = 0;
scanf("%d",&n);
for(j = 1; j<=n; j++)
{
for(i = 1;i <=j; i++)
{
ret*=i;
}
sum += ret;
}
printf("%d\n",sum);
return 0;
}
//此代码输入3得出的结果是15,有误
开始调试:
1、F10一步一步的看结果,看出来当i==3的时候,ret初识值是2有误
2、设置一个断点,条件是i==3,再观察ret的值,发现没有初始化ret,下面代码改正
int main()
{
int n = 0;
int i = 0;
int ret = 1;
int sum = 0;
int j = 0;
scanf("%d",&n);
for(j = 1; j<=n; j++)
{
ret = 1;
for(i = 1;i <=j; i++)
{
ret*=i;
}
sum += ret;
}
printf("%d\n",sum);
return 0;
}
5、案例二:下面的代码输出什么?
//下面的代码输出什么?
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 和arr[12] 的地址是一样的,如图:
- 1、i 和 arr是局部变量,局部变量是放在栈区上的
- 2、栈区内存的使用习惯是:先使用高地址空间,再使用低地址空间
- 3、数组随着下标的增长地址是由低到高变化
- 4、如果i和arr换一下定义的时候就不会碰到了
6、案例三:实现一个strcopy的函数
void my_strcopy(char* dest,char* soc)
{
while(*soc != '\0')
{
*dest = *soc;
dest++;
soc++;
}
*dest = *soc;
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello";
my_strcopy(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
【1】也可以进行优化为:
void my_strcopy(char* dest,char* soc)
{
while(*soc != '\0')
{
*dest++ = *soc++;
}
*dest = *soc;
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello";
my_strcopy(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
【2】再优化为:
void my_strcopy(char* dest,char* soc)
{
while(*dest++ = *soc++) //即把“\0”拷贝过去了,又停止了
{
;
}
*dest = *soc;
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello";
my_strcopy(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
【3】再优化为:
#include<assert.h>
void my_strcopy(char* dest,char* soc)
{
assert(soc != NULL); //防止arr2为NULL时候报错
while(*dest++ = *soc++)
{
;
}
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello";
my_strcopy(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
#理解const(断言)下列://const如果再*的左边
{
int num = 10;
const int* p = #
//p是指针变量
//*p是指针指向的内容
//const修饰子帧变量的时候
//const如果再*的左边,修饰的是*p,表示指针指向的内容,*p是不可以被改变的的
*p = 20; //这就话就会报错:“表达式必须是可修改的左值”
p = &n; // 这就话没有问题
return 0;
}
const如果放在*的右边
int main()
{
int num = 10;
int* const p = #
int n = 20;
//p是指针变量
//*p是指针指向的内容
//const修饰子帧变量的时候
//const如果放在*的右边即:int* const,就表示指针p不可以改变,但是指针指向的内容是可以改变的
*p = 20; //这句话没有问题
p = &n; //这就话就会报错:“表达式必须是可修改的左值”
return 0;
}
【4】再进行优化:
#include<assert.h>
void my_strcopy(char* dest,const char* soc) //加const把soc固定了,防止soc和dest写反
{
assert(soc != NULL);
while(*dest++ = *soc++)
{
;
}
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello";
my_strcopy(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
【5】再优化:
#include<assert.h>
//strcpy 这个库函数,其实返回的是目标空间的起始地址
char* my_strcopy(char* dest,const char* soc) //加const把soc固定了,防止soc和dest写反
{
char* ret = dest;
assert(soc != NULL);
assert(dest !=NULL);
while(*dest++ = *soc++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxx";
char arr2[] = "hello";
printf("%s\n",my_strcopy(arr1,arr2));
return 0;
}
7、实现一个函数strlen,用到优化:const 和assert
#include<assert.h>
int my_strlen(const char* str)
{
assert(str != NULL);
int count = 0;
while(*str != '\0')
{
str++;
count++;
}
return count;
}
int main()
{
char arr[] = "hello bite";
printf("%d\n",my_strlen(arr));
return 0;
}
8、常见的错误分类 :
编译型错误
直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。
链接型错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误。
运行时错误
借助调试,逐步定位问题。最难搞。