问题:采用递归方法计算给定整型数组元素之和。
以下给出几种递归算法的实现:
1 int sum1(int a[], int n)
2 {
3 if(n > 0)
4 return a[n-1] + sum1(a, n-1);
5 else
6 return 0;
7 }
8
9 int sum2(int a[], int n)
10 {
11 if(n > 0)
12 return a[n-1] + sum2(a, --n);
13 else
14 return 0;
15 }
16
17 int sum3(int a[], int n)
18 {
19 if(n > 0)
20 return a[--n] + sum3(a, n-1);
21 else
22 return 0;
23 }
24
25 int sum4(int a[], int n)
26 {
27 if(n > 0)
28 return a[n] + sum4(a, --n);
29 else
30 return 0;
31 }
32
33 int sum5(int a[], int n)
34 {
35 if(n > 0)
36 return a[--n] + sum5(a, n);
37 else
38 return 0;
39 }
40
41 int sum6(int a[], int n)
42 {
43 while(n > 0)
44 return a[n-1] + sum6(a, n-1);
45
46 return 0;
47 }
为验证和比较上述几种算法实现,编写测试代码如下:
1 int main(void)
2 {
3 int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
4 printf("sum1 = %d\n", sum1(a, 10));
5 printf("sum2 = %d\n", sum2(a, 10));
6 printf("sum3 = %d\n", sum3(a, 10));
7 printf("sum4 = %d\n", sum4(a, 10));
8 printf("sum5 = %d\n", sum5(a, 10));
9 printf("sum6 = %d\n", sum6(a, 10));
10 return 0;
11 }
在GCC3.2.3编译器下执行结果为:
1 sum1 = 55
2 sum2 = 55
3 sum3 = 30
4 sum4 = -1076625666(随机值)
5 sum5 = 55
6 sum6 = 55
而在VC6.0编译器下执行结果为:
1 sum1 = 55
2 sum2 = -858993415(固定值)
3 sum3 = 30
4 sum4 = 55
5 sum5 = 55
6 sum6 = 55
可见:
1. 对于a[n-1] + sum2(a, --n),GCC编译器先加法后将n自减,等效于a[n-1] + sum2(a, n-1);而VC编译器先将n自减,等效于a[n-2] + sum2(a, n-1);
2. 对于a[--n] + sum3(a, n-1),GCC编译器先将n自减,等效于a[n-1] + sum2(a, n-2);而VC编译器与之相同;
3.
不同的编译器对表达式和自减(或自增)运算符的处理顺序不同。因此,编程时算式中应采用直白易懂的写法(如sum1),避免使用自增/自减;
4. 递归算法中,sum1中的if与sum6中的while等效。
5. 遇到语法正确但运行诡异的问题时,不妨怀疑编译器是否存在bug。
最后,简要提及两种编译器下sum4和sum2执行后的异常值。分析函数调用过程可知,两个异常值均由数组越界导致(gcc-sum4的a[10],vc-sum2的a[-1])。-1076625666的16进制表示形式为0xbfd3fefe,很明显是个Linux栈区地址;而-858993415的16进制表示形式为0xccccccf9,0xcc是VC编译器Debug模式下系统为未初始化的栈区变量所赋的初值。此外,因栈区地址为运行时概念,故每次执行后sum4结果可能略有变化;而VC的0xcc特殊占位符取值固定,故sum2结果不随执行次数而变。
将测试代码稍加改造:
1 int main(void)
2 {
3 int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
4 int n = sizeof(a) / sizeof(a[0]);
5 printf("sum1 = %d\n", sum1(a, n));
6 printf("sum2 = %d\n", sum2(a, n));
7 printf("sum3 = %d\n", sum3(a, n));
8 printf("sum4 = %d\n", sum4(a, n));
9 printf("sum5 = %d\n", sum5(a, n));
10 printf("sum6 = %d\n", sum6(a, n));
11 return 0;
12 }
则GCC编译器会给出不同的结果:
1 sum1 = 55
2 sum2 = 55
3 sum3 = 30
4 sum4 = 64
5 sum5 = 55
6 sum6 = 55
因时间有限,本文仅就表面现象说明,未进行深入分析。读者若有兴趣可自行分析下背后的机理,分析手段参考如下文章: