我们日常写代码的时候,常常会遇到bug的情况,这个时候像我这样的初学者就会像无头苍蝇一样这里改改那里删删,调试的重要性也就显现出来,这篇文章接着上文来讲解。
大概分为以下几个部分:
5. 一些调试的实例。
6. 如何写出好(易于调试)的代码。
7. 编程常见的错误。
话不多说,现在开始!
5. 一些调试的实例
5.1 实例一
实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出:
我们失误写出下面的错误代码:
#include<stdio.h> 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,但实际输出的是15。
为什么呢?
首先推测问题出现的原因。初步确定问题可能的原因最好。
实际上手调试很有必要。
调试的时候我们要做到心里有数
我们小试牛刀调试一下,首先分析问题所在。 编译器没有报错,说明代码没有语法的问题。
输入3,按f11逐语句进行调试。
一次循环下来,sum和ret都变为1,i变为1。
第二次循环下来,仍然看不到什么问题,阶乘和其和也都正确。
在第三次循环,我们发现ret增长的速度十分的快,这才发现ret的值并没有重置为1 。
我们通过调试发现了错误,写出了正确的代码:
#include<stdio.h> int main() { int i = 0; int sum = 0; int n = 0; int ret = 1; scanf("%d", &n); for (i = 1; i <= n; i++) { int j = 0; ret = 1;//将ret=1置于循环里 for (j = 1; j <= i; j++) { ret *= j; } sum += ret; } printf("%d\n", sum); return 0; }
5.2.实例2
给出一个数组越界访问的例子,出自《C陷阱与缺陷》 。
#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; }
如图,程序会死循环打印hehe。
上一个代码可以不用调试看出来错误,但是这个是无法看出来的,只能调试来看。
可以看到到这一步为止都十分正常,也就是i==12之前是正常的。
然而这一步之后,i和arr[i]就一同变成了0。
知道了问题所在,我们这次通过地址来调试看看。
可以看到,当i==12时,i的地址和arr[i]的地址是相同的,也就是说,它们在栈区所开辟的空间相同。这样导致的结果就是, arr[i] = 0的操作将i也一同变成了0,导致死循环。