春暖花开
雪化了,花开了,春天来了
posts - 149,comments - 125,trackbacks - 0

今日从网上看到一篇好文章,汇总汇总,又拼凑拼凑,便有了下文。
 

static关键字是CC++中都存在的关键字, 它主要有三种使用方式, 其中前两种只指在C语言中使用, 第三种在C++中使用(C,C++中具体细微操作不尽相同,   本文以C++为准).  

(1) 局部静态变量 静态局部变量有两个用法,记忆功能和全局生存期.

(2) 外部静态变量/函数 用于全局变量,主要作用是限制此全局变量被其他的文件调用

(3) 静态数据成员/成员函数 表示这个成员是属于这个类但是不属于类中任意特定对象

 

下面就这三种使用方式及注意事项分别说明

 一、局部静态变量  
 
C/C++, 局部变量按照存储形式可分为三种auto, static, register  

 auto类型(普通)局部变量相比, static局部变量有三点不同

  1.   存储空间分配不同 

  auto类型分配在栈上, 属于动态存储类别, 占动态存储区空间, 函数调用结束后自动释放, static分配在静态存储区, 在程序整个运行期间都不释放. 两者之间的作用域相同, 但生存期不同.  

  2.   static局部变量在所处模块在初次运行时进行初始化工作, 且只操作一次 

  3.   对于局部静态变量, 如果不赋初值, 编译期会自动赋初值0或空字符, auto类型的初值是不确定的.(对于C++中的class对象例外, class的对象实例如果不初始化, 则会自动调用默认构造函数, 不管是否是static类型)  

 特点: static局部变量的记忆性与生存期的全局性”  

 所谓“记忆性是指在两次函数调用时, 在第二次调用进入时, 能保持第一次调用退出时的值.    

 示例程序一 

  #include   <iostream>  

  using   namespace   std;  

  void   staticLocalVar()  

  {  

static  int a  = 0; // 运行期时初始化一次,下次再调用时,不进行初始化工作 

cout<<"a="<<a<<endl;  

++a;  

}  

int   main()  

{  

    staticLocalVar();   //  第一次调用,   输出a=0  

    staticLocalVar();   // 第二次调用,   记忆了第一次退出时的值,   输出a=1  

    return   0;  

}  

 应用:  利用“记忆性”,   记录函数调用的次数(示例程序一)

       

利用生存期的“全局性,改善

return a   pointer   /   reference   to   a   local   object”的问题. Local object的问题在于退出函数, 生存期即结束,利用static的作用, 延长变量的生存期.  

 示例程序二:  

  //   IP   address   to   string   format  

  //   Used   in   Ethernet   Frame   and   IP   Header   analysis  

  const   char   *   IpToStr(UINT32   IpAddr)  

  {  

    static   char   strBuff[16];   //   static局部变量,   用于返回地址有效

    const  unsigned   char  *pChIP   =  (const   unsigned   char   *)&IpAddr;

   sprintf(strBuff, "%u.%u.%u.%u", pChIP[0], pChIP[1],  pChIP[2], pChIP[3]);  

    return   strBuff;  

  }  

 注意事项:  

  1. “记忆性”,  程序运行很重要的一点就是可重复性, static变量的记忆性破坏了这种可重复性,   造成不同时刻至运行的结果可能不同.  

  2.  “生存期全局性和唯一性. 普通的local变量的存储空间分配在stack,  因此每次调用函数时,  分配的空间都可能不一样, static具有全局唯一性的特点, 每次调用时, 都指向同一块内存, 这就造成一个很重要的问题   ----   不可重入性!!!  

 

 这样在多线程程序设计或递归程序设计中,  要特别注意这个问题.

 下面针对示例程序二,   分析在多线程情况下的不安全性.(为方便描述,   标上行号

const   char   *   IpToStr(UINT32   IpAddr)  

  {  

  static   char   strBuff[16];   //   static局部变量,   用于返回地址有效

    const  unsigned   char  *pChIP   =  (const   unsigned   char   *)&IpAddr;

   sprintf(strBuff, "%u.%u.%u.%u", pChIP[0], pChIP[1],  pChIP[2], pChIP[3]);  

    return   strBuff;  

  }  

 

   假设现在有两个线程A,B运行期间都需要调用IpToStr()函数, 32位的IP地址转换成点分10进制的字符串形式.  

A先获得执行机会, 执行IpToStr(), 传入的参数是0x0B090A0A, 顺序执行完应该返回的指针存储区内容是: “10.10.9.11”, 现执行到⑥时, 失去执行权, 调度到B线程执行, B线程传入的参数是0xA8A8A8C0,执行至⑦, 静态存储区的内容是192.168.168.168. 当再调度到A执行时, 从⑥继续执行, 由于strBuff的全局唯一性, 内容已经被B线程冲掉, 此时返回的将是192.168.168.168字符串, 不再是10.10.9.11字符串.  

补充:静态局部变量属于静态存储方式,它具有以下特点:
(1)
静态局部变量在函数内定义    它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后,尽管该变量还继续存在,但不能使用它。

(2)允许对构造类静态局部量赋初值    例如数组,若未赋以初值,则由系统自动赋以0值。
(3)
对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜

 

  二、外部静态变量/函数 

   Cstatic有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。 但为了限制全局变量/函数的作用域,  函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。注意此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区, 生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.  

 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

 示例程序三:  

  //file1.cpp  

  static   int   varA;  

  int   varB;  

  extern   void   funA()  

  {  

  ……  

  }  

  static   void   funB()  

  {  

  ……  

  }

 

  //file2.cpp  

  extern   int   varB;   //   使用file1.cpp中定义的全局变量 

  extern   int   varA;   //   错误! varAstatic类型,  无法在其他文件中使用 

  extern   vod   funA(); //   使用file1.cpp中定义的函数 

  extern   void   funB(); //   错误! 无法使用file1.cpp文件中static函数 

 

补充:全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。

 

  三、静态数据成员/成员函数(C++特有)  

  C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数. 这是与普通成员函数的最大区别, 也是其应用所在, 比如在对某一个类的对象进行计数时,计数生成多少个类的实例, 就可以用到静态数据成员. 在这里面, static既不是限定作用域的, 也不是扩展生存期的作用, 指示变量/函数在此类中的唯一性. 这也是属于一个类而不是属于此类的任何特定对象的变量和函数的含义.  因为它是对整个类来说是唯一的, 因此不可能属于某一个实例对象的. (针对静态数据成员而言, 成员函数不管是否是static,在内存中只有一个副本.普通成员函数调用时, 需要传入this指针,static成员函数调用时, 没有this指针.   )  

 请看示例程序四

  class   EnemyTarget   {  

  public:  

      EnemyTarget()   {   ++numTargets;   }  

      EnemyTarget(const   EnemyTarget&)   {   ++numTargets;   }  

      ~EnemyTarget()   {   --numTargets;   }  
      static   size_t   numberOfTargets()   {   return   numTargets;   }  

      bool  destroy(); //   returns  success  of  attempt  to  destroy  

// EnemyTarget   object  

      private:  

      static   size_t   numTargets;   //   object   counter  

  };  

  //   class   statics   must   be   defined   outside   the   class;  

  //   initialization   is   to   0   by   default  

  size_t   EnemyTarget::numTargets;  

 在这个例子中,   静态数据成员numTargets就是用来计数产生的对象个数的.  

在《c++ 程序设计语言》中,是这样运用的:
 

Static静态成员,它是类的一部分,但却不是该类的各个对象的一部分。一个static成员只有唯一的一份副本,但不像常规的非static成员那样在每个对象里各有一份副本。 与此类似,一个需要访问类成员,然而却并不需要针对特定对象去调用的函数,也被称为一个static成员函数。其好处在于消除了由于依赖全局量而引起的问题

Class Date

{

   Int d, m, y;

   Static Date default_date;

   Public:

      Date(int dd=0, int mm=0, int yy=0);

      //……

      Static void set_default(int, int, int);

};

静态成员可以像任何其他成员一样引用,此外,对于静态成员的引用不必提到任何对象,相反,在这里应该成员的名字加上作为限定词的类的名字。

Void f()

{

 Date::set_default(4, 5, 1945);

}

静态成员(包括函数和数据成员)都必须在某个地方另行定义。如

Date Date:::default_date(16, 12, 1770);

Void Date::set_default(int d, int m, int y)

{

 Date::default_date = Date(d, m, y);

}

 

补充:内部函数和外部函数

当一个源程序由多个源文件组成时,C语言根据函数能否被其它源文件中的函数调用,将函数分为内部函数和外部函数。
1
内部函数(又称静态函数)
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。
定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:
static   
函数类型    函数名(函数参数表)
{……}
关键字“static”,译成中文就是静态的,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。

2 外部函数
外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
[extern]   
函数类型    函数名(函数参数表)
{……}
调用外部函数时,需要对其进行说明:
[extern]   
函数类型    函数名(参数类型表)[,函数名2(参数类型表2)……]

 

posted on 2008-10-11 10:40 Sandy 阅读(342) 评论(1)  编辑 收藏 引用

FeedBack:
# re: static 用法总结
2008-10-11 10:45 | gggggggggggggg
nice  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理