目录
4.1 实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出
1.调试是什么?
调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序
错误的一个过程
2. 调试的基本步骤
发现程序错误的存在
以隔离、消除等方式对错误进行定位
确定错误产生的原因
提出纠正错误的解决办法
对程序错误予以改正,重新测试
3.Debug和Release的介绍
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优
的,以便用户很好地使用。
4. 调试实例
4.1 实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int ret = 1;
int sum = 0;
for (n = 1; n <= 3; n++)
{
int i = 0;
for (i = 1; i <= n; i++)
{
ret *= i;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
1!+2!+3!= 9 但上述代码算出的结果是15.这说明我们的代码出了问题,开始进行调试。
当我们进行计算3的阶乘的时候,ret的值还是2,这种代码的写法是从1一直乘到所求的那个数上去,所以我们要将每次进入循环的ret的值设为1。
正确代码
int main()
{
int n = 0;
scanf("%d", &n);
int ret = 1;
int i = 0;
int sum = 0;
for (i = 1; i <= n; i++)
{
ret *= i;
sum += ret;
}
printf("%d", sum);
return 0;
}
4.2 分析这段代码
#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;
}
首先,我们很容易就看出数组越界访问了,但在组代码的结果在不同环境下的结果是不一样的。在vs中,它是会死循环的。
解释:因为内存中栈区的使用习惯是先使用高地址空间,再使用低地址空间,于是乎操作系统就在栈中就先分配给了局部变量i一块空间,随后又给数组分配了一块空间,而内存中数组是连续存放的,它随着下标的增长地址由低到高。而我们在数组中放了元素0之后,在vs中数组越界了,vs这个编译器依然会给数组越界后的内存放上数字0,但是vs中变量和数组在内存中的位置相差两个字节,当程序到arr[12]的时候,实际上是访问到了变量i的空间,此时将arr[12]中放上数据0,实际上就是将变量 i 中的值修改为了0,因为此时两者指向的是同一块内存空间。
再次注意,不同的编译环境中,结果可能是不同的。在vc6.0中 i 和 arr 在内存中是没有空隙的。
而在gcc中,两者之间相差一个字节,在vs中则相差两个字节。
5.如何写出好(易于调试)的代码。
5.1 优秀的代码:
1. 代码运行正常
2. bug很少
3. 效率高
4. 可读性高
5. 可维护性高
6. 注释清晰
7. 文档齐全
常见的coding技巧:
1. 使用assert
2. 尽量使用const
3. 养成良好的编码风格
4. 添加必要的注释
5. 避免编码的陷阱。
5.2 示范
模拟实现strcpy,博主将展示不断优化的代码
博主将展示不断优化的代码,满分为10分
方法一,5分
void my_strcpy(char* dest, char* src)//传进来两个指针
{
while (*src != '\0')
{
*dest = *src;
src++;
dest++;
}
*dest = *src; //这里最后将src指向的'\0'赋给dest指向的区域
}
int main()
{
char arr1[] = "hello haha";
char arr2[20] = "xxxxxxxxxxxxxx";
my_strcpy(arr2,arr1);
printf("%s\n", arr2);
return 0;
}
方法二,6分
void my_strcpy(char* dest, char* src)//传进来两个指针
{
while (*src != '\0')
{
*dest++ = *src++;
}
*dest = *src; //这里最后将src指向的'\0'赋给dest指向的区域
}
int main()
{
char arr1[] = "hello haha";
char arr2[20] = "xxxxxxxxxxxxxx";
my_strcpy(arr2,arr1);
printf("%s\n", arr2);
return 0;
}
方法三,7分
void my_strcpy(char* dest, char* src)//传进来两个指针
{
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "hello haha";
char arr2[20] = "xxxxxxxxxxxxxx";
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
解释一下,这句语句,其实就是先将*src赋给*dest,然后src++,dest++,当src指向‘\0’的时候,先将'\0'赋给dest,但是此时*dest++ = *src++ 这个表达式的值为0,循环就结束了。
方法四,8分
#include <stdio.h>
#include <assert.h>
void my_strcpy(char* dest, char* src)
{
assret(src != NULL);
assret(dest != NULL);
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "hello haha";
char arr2[20] = "xxxxxxxxxxxxxx";
int* p = NULL;
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
这里,用到了一个函数assert(断言),这样做的原因是我们在传参中,有时候会误将空指针给传进去,这时候就会出现读写异常,而使用assert之后呢,可以快速的帮我们定位到问题的所在,因为当你传了空指针时,连编译都不让你编过去。
方法5, 9分
void my_strcpy(char* dest, char* src)
{
assret(src && dest);
while (*dest++ = *src++)
{
;
}
}
int main()
{
char arr1[] = "hello haha";
char arr2[20] = "xxxxxxxxxxxxxx";
//int* p = NULL;
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
这里就是将两个assert组合了起来。
方法6,满分
不知道你有没有注意到strcpy返回的是一个字符型的指针,还有src前面有一个const修饰。我们要模拟就要做到最好。 my_strcpy函数设计返回值类型是为了实现函数的链式访问。
#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(src && dest);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "hello haha";
char arr2[20] = "xxxxxxxxxxxxxx";
//int* p = NULL;
printf("%s\n", my_strcpy(arr2, arr1));
return 0;
}
6.补充知识
6.1 const讲解
int main()
{
//const int num = 10; //这里编译不同的,是因为表达式必须是可修改的值
//num = 20; //这里的num此时已经是常变量了,所以不可被修改。
int num = 20;
int n = 0;
const int* p1 = #
p1 = &n;
int* const p2 = #
*p2 = 10;
return 0;
}
const 可以修饰指针
const 放在*的左边(const int* p1;)
const修饰的是*p1,表示p1指向的对象不能通过p1来改变,但是p1变量中的地址是可以改变的
const 放在*的右边(int* const p2;)
const 修饰的是p2,表示p2的内容不能被改变,但是p2指向的的对象是可以通过p来改变的
7.编译常见的错误
编译型错误就是语法错误
链接型错误
运行时错误 - 借助调试解决的错误
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
void* p;
void test(void)
{
printf("hehe\n");
}
int main()
{
return 0;
}