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];   
}
posted @ 2011-10-12 22:34  garbageMan  阅读(4502)  评论(58编辑  收藏  举报