5、一些调试的实例。
5.1 实例一
实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出。
演示错误代码:
1. #include<stdio.h> 2. int main() 3. { 4. int i = 0; 5. int sum = 0;//保存最终结果 6. int n = 0; 7. int ret = 1;//保存n的阶乘 8. scanf("%d", &n); 9. for (i = 1; i <= n; i++) 10. { 11. int j = 0; 12. for (j = 1; j <= i; j++) 13. { 14. ret *= j; 15. } 16. sum += ret; 17. } 18. printf("%d\n", sum); 19. return 0; 20. }
很明显,结果是不对的,那么我们怎么去找毛病所在呢?调试!!!
首先,我们打开监视,看看过程中是什么原因:
我们可以看到当i=2的时候一切还正常,接下来:
当i=3的时候我们看到ret=12!!
这说明了什么问题?问题出在ret上,因为ret一直在累加,每一次循环结束后,ret的值并没有归1,这就导致后面的数值变大,所以,正确的应该是这样的:
1. #include<stdio.h> 2. int main() 3. { 4. int i = 0; 5. int sum = 0;//保存最终结果 6. int n = 0; 7. int ret = 1;//保存n的阶乘 8. scanf("%d", &n); 9. for (i = 1; i <= n; i++) 10. { 11. ret = 1; 12. int j = 0; 13. for (j = 1; j <= i; j++) 14. { 15. ret *= j; 16. } 17. sum += ret; 18. } 19. printf("%d\n", sum); 20. return 0; 21. }
5.2 实例二
1. #include <stdio.h> 2. int main() 3. { 4. int i = 0; 5. int arr[10] = {0}; 6. for(i=0; i<=12; i++) 7. { 8. arr[i] = 0; 9. printf("hehe\n"); 10. } 11. return 0; 12. }
代码死循环的原因上面已经讲过了!这是一个典型的调试题。
6、如何写出好(易于调试)的代码。
6.1 优秀的代码:
1. 代码运行正常
2. bug很少
3. 效率高
4. 可读性高
5. 可维护性高
6. 注释清晰
7. 文档齐全
常见的coding技巧:
1. 使用assert
2. 尽量使用const
3. 养成良好的编码风格
4. 添加必要的注释
5. 避免编码的陷阱。
6.2 示范:
模拟实现库函数:strcpy
首先我们打开MSDN看一下strcpy的信息:
没有下载的小伙伴可以看我往期的一个博客里面有链接,现在放下面了:
https://blog.csdn.net/m0_67995737/article/details/124789657
1. 2. char* strcpy(char* dst, const char* src) 3. { 4. char* cp = dst; 5. assert(dst && src); 6. while (*cp++ = *src++) 7. ; /* Copy src over dst */ 8. return(dst); 9. }
其中assert函数是断言的意思,当assert出现错误时,就会报错,帮助提醒你进行修改,这里是防止空指针的情况!
6.3 const的作用
1. #include <stdio.h> 2. //代码1 3. void test1() 4. { 5. int n = 10; 6. int m = 20; 7. int* p = &n;//可以更改p的指向,也可以更改p所指内存的数值 8. *p = 20;//ok 9. p = &m; //ok 10. } 11. void test2() 12. { 13. //代码2 14. int n = 10; 15. int m = 20; 16. const int* p = &n;//可以更改p的指向,但不能更改p所指内存的数值 17. *p = 20;//err 18. p = &m; //ok 19. } 20. void test3() 21. { 22. int n = 10; 23. int m = 20; 24. int* const p = &n;//可以修改p所指内存的数值,但不能更改p的指向 25. *p = 20; //ok 26. p = &m; //err 27. } 28. int main() 29. { 30. //测试无cosnt的 31. test1(); 32. //测试const放在*的左边 33. test2(); 34. //测试const放在*的右边 35. test3(); 36. return 0; 37. }
结论:
const修饰指针变量的时候:
1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改
变。但是指针变量本身的内容可变。
2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指
针指向的内容,可以通过指针改变。
6.4模拟实现一个strlen函数
参考代码:
1. #include <stdio.h> 2. #include<assert.h> 3. int my_strlen(const char* str) 4. { 5. int count = 0; 6. assert(str != NULL); 7. while (*str)//判断字符串是否结束 8. { 9. count++; 10. str++; 11. } 12. return count; 13. } 14. int main() 15. { 16. const char* p = "abcdef"; 17. //测试 18. int len = my_strlen(p); 19. printf("len = %d\n", len); 20. return 0; 21. }
7、编程常见的错误
7.1 编译型错误
直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。
这个很常见:
比如:
像这种的就叫编译型错误。
7.2 链接型错误
看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不
存在或者拼写错误。
比如这个
7.3 运行时错误
前面列举的调试中的问题就是运行时的错误。
借助调试,逐步定位问题。最难搞。
温馨提示:
做一个有心人,积累排错经验。