八,调试
一,调试实战
1.例子1
#include<stdio.h>
int main()
{
int i=0;
int arr[10]={0};
for(i=0;i<12;i++)
{
arr[i]=0;
printf("hehe\n");
}
return 0;
}
以下代码有两个问题:1. 数组访问越界 2. 死循环
以下代码再vs2013下会造成死循环,原因:
栈内存:
|CC CC CC CC|
arr[0]|01 00 00 00|\
arr[1]|02 00 00 00| \
arr[2]|03 00 00 00| \
arr[3]|04 00 00 00| \
arr[4]|05 00 00 00| \
arr[5]|06 00 00 00| / arr的空间
arr[6]|07 00 00 00| /
arr[7]|08 00 00 00| /
arr[8]|09 00 00 00| /
arr[9]|0A 00 00 00|/
|CC CC CC CC|
|CC CC CC CC|
|00 00 00 00| i的空间
|CC CC CC CC|
for循环中,i的内容是从0,一直增加到12,而数组只有10个空间,因此会越界
每次访问arr数组i号位置时,都会将该位置内容设置为0,当访问到arr[12]时,
也会将该位置内容设置为0,而位置恰好为i的位置,即a[12]恰巧将i设置为0,因此造成死循环。
更进一步解释:i和arr是局部变量,是在内存中栈区存放的,而栈区的使用习惯是:先使用高地址处的空间,在使用低地址的空间。
又因为数据随着下标的增长,地址是从低到高变化的,所以有可能访问到i上,然后令i=0,无线循环。
2.例子2
#include <stdio.h>
int main()
{
int i = 0;
int sum = 0;
int n = 0;
int ret = 1;
scanf("%d", &n);
for (i = 0; i <= n; i++)
{
int j = 0;
for (j = 1; j <= i; j++)
{
ret = ret * j;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
修改结果:
#include <stdio.h>
int main()
{
int i = 0;
int sum = 0;
int n = 0;
int ret = 0;
scanf("%d", &n);
for (i = 0; i <= n; i++)
{
int j = 1;
if (j <= i)
{
for (j = 1, ret = 1; j <= i; j++)
{
ret = ret * j;
}
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
#include <stdio.h>
int main()
{
int i, sum = 0, n, ret;
scanf("%d", &n);
for (i = 1, ret = 1; i <= n; i++)
{
ret *= i;
sum += ret;
}
printf("%d\n", sum);
return 0;
}
修改过程:
问题很多:
发现问题一:多加了个(ret=1)
发现问题二:ret没有清0
解决篇:
解决问题尝试一:解决ret没有清0
解决问题尝试二:把多加的解决掉
二,什么优秀的代码?怎么写出优秀的代码?
优秀的代码:
1.代码正常运行
2.bug少
3.效率高
4.可读性高
5.可维护性高
6.注释清晰
7.文档齐全
2.常用的coding技巧:
-
使用assert
-
使用const
-
养成良好的编码风格
-
添加必要的注释
-
避免代码陷阱
示范:
-
使用assert(断言)
#include <stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
//断言
assert(dest != NULL);
assert(src != NULL);
while (*src != ‘\0’)
{
*dest = *src;
dest++;
src–;
}
*dest = *src;// \0的拷贝}
int main()
{
char arr1[] = “hello world”;
char arr2[20] = “xxxxxxxxxxxxxxxx”;
char* p = NULL;
printf(“%s\n”, my_strcpy(p, arr1));return 0;
}
直接会报错
#include <stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
//断言
assert(dest != NULL);
assert(src != NULL);
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;// \0的拷贝
}
int main()
{
char arr1[] = "hello world";
char arr2[20] = "xxxxxxxxxxxxxxxx";
char* p = NULL;
my_strcpy(arr2, arr1);
printf("%s\n",arr2 );
return 0;
}
#include <stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
//断言
assert(dest != NULL);
assert(src != NULL);
while (*dest = *src)
{
dest++;
src++;
}
}
int main()
{
char arr1[] = "hello world";
char arr2[20] = "xxxxxxxxxxxxxxxx";
char* p = NULL;
my_strcpy(arr2, arr1);
printf("%s\n",arr2 );
return 0;
}
#include <stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret=dest;
//断言
assert(dest != NULL);
assert(src != NULL);
while(*dest++ = *src++)
;//空语句
return ret;
}
int main()
{
char arr1[] = "hello world";
char arr2[20] = "xxxxxxxxxxxxxxxx";
char* p = NULL;
printf("%s\n", my_strcpy(arr2, arr1));
return 0;
}
- 使用const(保护作用)
const如果放在*左边那么说明维护的是指针,不能改变指针指向的内容,可以改变指针变量本身的内容
const如果放在*右边那么说明维护的是指针变量本身的内容,不能改变改变指针变量本身的内容,可以指针指向的内容
#include <stdio.h>
void test1()
{
int n = 10;
int m = 20;
int* p = &n;
*p = 20; //ok
p = &m; //ok
}
void test2()
{
int n = 10;
int m = 20;
const int* p = &n;
*p = 20; //错误
p = &m; //ok
}
void test3()
{
int n = 10;
int m = 20;
int*const p = &n;
*p = 20; //ok
p = &m; //错误
}
int main()
{
test1();
test2();
test3();
return 0;
}
3.编码常见的错误
- 编译型错误、
直接看错误信息,解决问题
- 链接型错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。
一般是标识符不存在或者拼写错误
- 运行时错误
借助调试,逐步定位问题