zoukankan      html  css  js  c++  java
  • 运算顺序引发的一系列有趣问题

         问题:采用递归方法计算给定整型数组元素之和

         以下给出几种递归算法的实现:

     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
    ", sum1(a, 10));
     5     printf("sum2 = %d
    ", sum2(a, 10));
     6     printf("sum3 = %d
    ", sum3(a, 10));
     7     printf("sum4 = %d
    ", sum4(a, 10));
     8     printf("sum5 = %d
    ", sum5(a, 10));
     9     printf("sum6 = %d
    ", 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
    ", sum1(a, n));
     6     printf("sum2 = %d
    ", sum2(a, n));
     7     printf("sum3 = %d
    ", sum3(a, n));
     8     printf("sum4 = %d
    ", sum4(a, n));
     9     printf("sum5 = %d
    ", sum5(a, n));
    10     printf("sum6 = %d
    ", 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

     

         因时间有限,本文仅就表面现象说明,未进行深入分析。读者若有兴趣可自行分析下背后的机理,分析手段参考如下文章:

     

  • 相关阅读:
    Nbear实体和接口 CodeSmith模版
    prototype1.4版中文参考手册(word,pdf,chm)
    SharePoint 2013 (SharePoint 15)的新特性
    没有域环境下安装SharePoint 2010
    产品经理(PM)常用原型图设计工具
    【转贴】mysql导入数据load data infile用法
    重新学javaweb!
    关于HIbernate中的lazy属性的一些解释
    JAVA程序员基本测试题目
    添加sql server约束
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/3782438.html
Copyright © 2011-2022 走看看