zoukankan      html  css  js  c++  java
  • static的滥用与变态的阉割

    什么情况下需要用局部静态变量呢?需要保留函数上一次调用结束时的值时,例如可以用下面的方法求n!。
    例7.17 输出1到5的阶乘值。

    #include <stdio.h>
    int main( void )
    {int fac(int n);
    int i;
    for(i=1;i<=5;i++)
    printf("%d!=%d\n" ,i,fac(i) );
    return 0;
    }
    int fac(int n)
    {static int f=1;
    f=f*n;
    return(f);
    }

    ————谭浩强 ,《C程序设计》(第四版),清华大学出版社, 2010年6月,p207

        这段程序的输出结果是:
        1!=1
        2!=2
        3!=6
        4!=24
        5!=120
        看起来似乎没有问题,然而稍微仔细思考一下就不难发现,如果若问题再补充一个要求时,比如在求完1到5的阶乘后再重新求3的阶乘时,那个fac()函数彻底报废、毫无用处。而造成这种结局的原因则是fac()函数对static莫名其妙的滥用,亦即在不该使用static局部变量时使用了static局部变量。
        为了进一步分析这种写法的荒谬,下面把fac()函数的内容等价地还原到调用者main()函数中,这样代码就变成了

    #include <stdio.h>
    int main( void )
    {
      int i;
      for ( i = 1 ; i <= 5 ; i++ )
        {
         static int f = 1 ;    
         f *= i ;
         printf("%d!=%d\n" , i , f ); 
        }
      return 0;
    }
    

        这段代码与样本中代码在功能上是等效的,但却没有样本代码的毛病。从这段代码中不难看出代码段

      for ( i = 1 ; i <= 5 ; i++ )
        {
         static int f = 1 ;    
         f *= i ;
         printf("%d!=%d\n" , i , f ); 
        }
    

    是一个有机的功能整体,其中i的取值顺序、static局部变量f及求阶乘的运算“f *=i”三者之间互相依赖,唇齿相依。
        样本代码的荒谬则在于把紧密依赖、三位一体的东西变态地阉割出一部分来,并把阉割出的部分写成了只能一次性使用的“残疾”函数。由于阉割出的部分包含static局部变量,fac()函数的行为对调用次序具有强烈的依赖性,函数就其本性来说根本不应该具有这种娇生惯养的依赖性。在大多数情况下,只要函数的调用者提供正确的实参,函数都应该产生正确的副效应和返回值,这才是函数的本分。它与调用者之间只应该存在必要的联系,所谓“必要”就是不多也不少的参数,除此之外不应该再有什么别的联系,这就是所谓的“低耦合”的含义。一旦违背了这个原则,比如样本代码那样阉割不可分的功能,就必然导致变态的强耦合联系方式。在不满足这种苛刻的强耦合条件下,函数就会变得毫无用处。这就好比把一个完整的计算机主板掰成两半,再用胶水粘合在一起,摆摆样子可以,但主板被分开的任何一部分都已经成了废品。在函数中滥用static局部变量,最终的效果多半如此。
        那么,是否static局部变量绝对不可以使用呢?倒也不是。但总的来说static局部变量的适应范围较窄。下面的例子中对static局部变量的使用就非常恰当,无论是把数组name设置为局部还是把它设定为static存储类别都非常恰到好处。

    #include <stdio.h>
    
    char * month(unsigned) ;
    
    int main( void )
    {
      puts( month(10) );
      return 0;
    }
    
    char * month(unsigned m) 
    {
      static char * const name[12] 
      = {"Jan","Feb","Mar","Apr","May","Jun",
         "Jul","Aug","Sep","Oct","Nov","Dec",};
      return name[m-1];   
    }
    
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 Torry的困惑(提高型)
    Java实现 蓝桥杯VIP 算法提高 Torry的困惑(提高型)
    Java实现 蓝桥杯VIP 算法提高 Torry的困惑(提高型)
    Java实现 蓝桥杯VIP 算法提高 计算时间
    关于编译器和链接器的一个实验
    Windows下获取逻辑cpu数量和cpu核数量(用GetLogicalProcessorInformation,从XP3才开始有的API)
    计算机底层数据的处理方式(汇编后将所有数据都转化为补码二进制数据,所有类型信息都会消失)
    值得推荐的C/C++框架和库
    Delphi子类调用祖父类的虚函数
    [Framework Design Guideline]
  • 原文地址:https://www.cnblogs.com/pmer/p/2209454.html
Copyright © 2011-2022 走看看