static 用法小結

static關鍵字是C,   C++中都存在的關鍵字,   它主要有三種使用方式,   其中前兩種只指在C語言中使用,   第三種在C++中使用(C,C++中具體細微操作不盡相同,   本文以C++爲準).  
  (1)局部靜態變量  
  (2)外部靜態變量/函數  
  (3)靜態數據成員/成員函數  
  下面就這三種使用方式及注意事項分別說明  
   
  一、局部靜態變量  
  在C/C++中,   局部變量按照存儲形式可分爲三種auto,   static,   register  
  (<C語言程序設計(第二版)>譚浩強,   第174-175頁)  
  與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具有全局唯一性的特點,   每次調用時,   都指向同一塊內存,   這就造成一個很重要的問題   ----   不可重入性!!!  
  這樣在多線程程序設計或遞歸程序設計中,   要特別注意這個問題.  
  (不可重入性的例子可以參見<effective   C++   (2nd)>(影印版)第103-105頁)  
  下面針對示例程序二,   分析在多線程情況下的不安全性.(爲方便描述,   標上行號)  
  ①   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字符串.  
   
  二、外部靜態變量/函數  
  在C中static有了第二種含義:用來表示不能被其它文件訪問的全局變量和函數。,   但爲了限制全局變量/函數的作用域,   函數或變量前加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;   //   錯誤!   varA是static類型,   無法在其他文件中使用  
  extern   vod   funA();   //   使用file1.cpp中定義的函數  
  extern   void   funB();   //   錯誤!   無法使用file1.cpp文件中static函數  
   
     
   
  三、靜態數據成員/成員函數(C++特有)  
  C++重用了這個關鍵字,並賦予它與前面不同的第三種含義:表示屬於一個類而不是屬於此類的任何特定對象的變量和函數.   這是與普通成員函數的最大區別,   也是其應用所在,   比如在對某一個類的對象進行計數時,   計數生成多少個類的實例,   就可以用到靜態數據成員.   在這裏面,   static既不是限定作用域的,   也不是擴展生存期的作用,   而是指示變量/函數在此類中的唯一性.   這也是”屬於一個類而不是屬於此類的任何特定對象的變量和函數”的含義.   因爲它是對整個類來說是唯一的,   因此不可能屬於某一個實例對象的.   (針對靜態數據成員而言,   成員函數不管是否是static,   在內存中只有一個副本,   普通成員函數調用時,   需要傳入this指針,   static成員函數調用時,   沒有this指針.   )  
  請看示例程序四(<effective   c++   (2nd)>(影印版)第59頁)  
  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就是用來計數產生的對象個數的.  
  另外,   在設計類的多線程操作時,   由於POSIX庫下的線程函數pthread_create()要求是全局的,   普通成員函數無法直接做爲線程函數,   可以考慮用Static成員函數做線程函數.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章