static C/C++剖析

c/c++中static詳解(轉載) 

 

 

一. 程序的存儲。

  

    從歷史上講,C程序一直由下面幾部分組成:

1. 正文段
      CPU執行的機器指令部分。通常,正文段是可共享的,所以即使是經常環境指針環境表環境字符串執行的程序(如文本編輯程序、C編譯程序、s h e l l等)在存儲器中也只需有一個副本,另外,正文段常常是隻讀的,以防止程序由於意外事故而修改其自身的指令。

2. 初始化數據段
      通常將此段稱爲數據段,它包含了程序中需賦初值的變量。初始化全局變量 靜態變量存放在這裏。例如,C程序中任何函數之外的說明:int maxcount = 99; 使此變量以初值存放在初始化數據段中。
   a.初始化的全局變量
   b.初始化的靜態變量

3. 非初始化數據段
     通常將此段稱爲bss段,這一名稱來源於早期彙編程序的一個操作符,意思是“block started by symbol(由符號開始的塊)”,未初始化全局變量靜態變量存放在這裏。在程序開始執行之前,內核將此段初始化爲0。函數外的說明:long sum[1000] ; 使此變量存放在非初始化數據段中。
   a.未初始化的全局變量
   b.未初始化的靜態變量

4. 堆
      需要由程序員分配釋放管理 ,若程序員不釋放,程序結束時可能由OS回收。通常在堆中進行動態存儲分配。
如程序中的malloc, calloc, realloc等函數都從這裏面分配。堆是從下向上分配的。

5. 棧
      由編譯器自動分配釋放管 理。局部變量及每次函數調用時返回地址、以及調用者的環境信息(例如某些機器寄存器)都存放在棧中。新被調用的函數在棧上爲其自動和臨時變量分配存儲空間。通過以這種方式使用棧,C函數可以遞歸調用。遞歸函數每次調用自身時,就使用一個新的棧幀,因此一個函數調用實例中的變量集不會影響另一個函數調用實例中的變量。
   a.局部變量
   b.函數調用時返回地址
   c.調用者的環境信息(例如某些機器寄存器)

二、C 語言中的static。

1. static 局部變量

    靜態局部變量屬於靜態存儲方式,它具有以下特點:
(1)靜態局部變量 在函數內定義它的生存期 整個程序生命週期,但是其 作用域仍與 自動變量相同 ,只能在定義該變量的函數內使用該變量。退出該函數後,儘管該變量還繼續存在,但不能使用它。
(2)對基本類型的靜態局部變量若在聲明時未賦以初值,則系統自動賦予0值 。而對自動變量不賦初值,則其值是不定的。


根據靜態局部變量的特點,可以看出它是一種生存期爲整個程序生命週期。雖然離開定義它的函數後不能使用,但如再次調用定義它的函數時,它又可繼續使用, 而且保存了前次被調用後留下的值。因此,當多次調用一個函數且要求在調用之間保留某些變量的值時,可考慮採用靜態局部變量。雖然用全局變量也可以達到上述目的,但全局變量有時會造成意外的副作用,因此仍以採用局部靜態變量爲宜。

例如:

  1. void test_static()  
  2. {  
  3.     staticint Temp = 1;  
  4.     Temp++;  
  5.     printf("Temp is :%d/n",Temp);  
  6. int main(int argc,char *argv[])  
  7. {  
  8.     int i=0;  
  9.     for(i=0;i<=4;i++)  
  10.     {  
  11.         test_static();  
  12.     }  
  13.     system("pause");  

事實上,static int Temp = 1;這句只會在第一次調用的時候纔會執行。

從以上分析可以看出,把局部變量改變爲靜態變量後是改變了它的存儲方式即改變了它的生存期


2. static 全局變量
     全局變量(外部變量)的說明之前再冠以static 就構成了靜態的全局變量。全局變量本身就是靜態存儲方式, 靜態全局變量當然也是靜態存儲方式。 這兩者在存儲方式上並無不同。
這兩者的區別在於:
(1). 非靜態全局變量作用域是整個源程序 ,當一個源程序由多個源文件 組成時,非靜態的全局變量在各個源文件中都是有效的。
(2). 而靜態全局變量 則限制了其作用域,即只在定義該變量的源文件內有效,在同一源程序的其它源文件中不能使用它。由於靜態全局變量的作用域侷限於 一個源文件內 ,只能爲該源文件內的函數公用, 因此可以避免在其它源文件中引起錯誤。


從以上分析可以看出,把全局變量改變爲靜態變量後是改變了它的作用域, 限制了它的使用範圍

3. static 函數

    如果在一個源文件中定義的函數,只能被本文件中的函數調用,而不能被同一程序其它文件中的函數調用,這種函數稱爲static函數與稱爲靜態函數。
定義一個static函數,只需在函數類型前再加一個“static”關鍵字即可,如下所示:
static  函數類型  函數名(函數參數表)
{……}
關鍵字“static”,譯成中文就是“靜態的”,所以內部函數又稱靜態函數。但此處“static”的含義不是指存儲方式,而是指對函數的作用域僅侷限於本文件


使用內部函數的好處是:不同的人編寫不同的函數時,不用擔心自己定義的函數,是否會與其它文件中的函數同名,因爲同名也沒有關係



二、C++ 中的static關鍵字(類中的static關鍵字)

1、static 數據成員

     在類內數據成員的聲明前加上關鍵字static,該數據成員就是類內的靜態數據成員。先舉一個靜態數據成員的例子。

  1. #include <iostream.h> 
  2. class Myclass 
  3. public
  4. Myclass(int a,int b,int c); 
  5. void GetSum(); 
  6. private
  7. int a,b,c; 
  8. staticint Sum;//聲明靜態數據成員 
  9. }; 
  10. int Myclass::Sum=0; //定義並初始化靜態數據成員 
  11. //static int Myclass::Sum = 0; //注意加static, 是錯誤的 
  12. Myclass::Myclass(int a,int b,int c) 
  13. this->a=a; 
  14. this->b=b; 
  15. this->c=c; 
  16. Sum+=a+b+c; 
  17. void Myclass::GetSum() 
  18. cout<<"Sum="<<Sum<<endl; 
  19. void main() 
  20. Myclass M(1,2,3); 
  21. M.GetSum();// cout 6 
  22. Myclass N(4,5,6); 
  23. N.GetSum();// cout 21 
  24. M.GetSum();// cout 21 

可以看出,static數據成員有以下特點:
(1). 對於非static數據成員,每個類對象都有自己的拷貝。而static數據成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態數據成員在程序中也只有一份拷貝 ,由該類型的所有對象共享訪問。也就是說,靜態數據成員是該類的所有對象所共有的。對該類的多個對象來說,靜態數據成員只分配一次內存,供所有對象共用。


(2). 靜態數據成員存儲在全局數據區。靜態數據成員定義時才分配空間,所以不能在類聲明中定義 。在上例中,語句int Myclass::Sum = 0;是定義靜態數據成員;


(3). 靜態數據成員和普通數據成員一樣遵從public,protected,private訪問規則


(4). 因爲靜態數據成員在全局數據區分配內存,屬於本類的所有對象共享,所以,它不屬於特定的類對象,在沒有產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它;


(5). 靜態數據成員初始化與一般數據成員初始化不同。靜態數據成員初始化的格式爲:
<數據類型><類名>::<靜態數據成員名>=<值>
如:int Myclass::Sum=0;


(6). 類的靜態數據成員有兩種訪問形式:
<類對象名>.<靜態數據成員名> 或 <類類型名>::<靜態數據成員名>
M.Sum = 0 或 Myclass::Sum = 0 (但是上面這個例子是不行的,因爲他是private的變量)如果靜態數據成員的訪問權限允許的話(即public的成員),可在程序中,按上述格式來引用靜態數據成員 ;


(7). 靜態數據成員主要用在各個對象都有相同的某項屬性的時候。比如對於一個存款類,每個實例的利息都是相同的。所以,應該把利息設爲存款類的靜態數據成員。這有兩個好處,第一,不管定義多少個存款類對象,利息數據成員都共享分配在全局數據區的內存,所以節省存儲空間。第二,一旦利息需要改變時,只要改變一次,則所有存款類對象的利息全改變過來了;


(8). 同全局變量相比,使用靜態數據成員有兩個優勢:
     a. 靜態數據成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字衝突的可能性;
     b. 可以實現信息隱藏。靜態數據成員可以是private成員,而全局變量不能;


2、static 成員函數
     static 成員函數,它爲類的全部服務而不是爲某一個類的具體對象服務。普通的成員函數一般都隱含了一個this指針,因爲普通成員函數總是具體的屬於某個類的具體對象的。通常情況下,this是缺省的。如函數fn()實際上是this->fn()。但是與普通函數相比,靜態成員函數由於不是與任何的對象相聯繫,因此它不具有this指針 。從這個意義上講,它無法訪問屬於類對象的no-static數據成員,也無法訪問no-static成員函數,它只能調用其餘的靜態成員函數 。下面舉個靜態成員函數的例子。

  1. #include <iostream.h> 
  2. class Myclass 
  3. public
  4. Myclass(int a,int b,int c); 
  5. staticvoid GetSum(); /聲明靜態成員函數 
  6. private
  7. int a,b,c; 
  8. staticint Sum;//聲明靜態數據成員 
  9. }; 
  10. int Myclass::Sum = 0;//定義並初始化靜態數據成員 
  11. Myclass::Myclass(int a,int b,int c) 
  12. this->a=a; 
  13. this->b=b; 
  14. this->c=c; 
  15. Sum+=a+b+c;//非靜態成員函數可以訪問靜態數據成員 
  16. //static void Myclass::GetSum(){...} //加上static是錯誤的 
  17. void Myclass::GetSum() //靜態成員函數的實現 
  18. //cout<<a<<endl; //錯誤代碼,a是非靜態數據成員 
  19. cout<<"Sum="<<Sum<<endl;//靜態函數是能訪問靜態數據成員 
  20. void main() 
  21. Myclass M(1,2,3); 
  22. M.GetSum(); 
  23. Myclass N(4,5,6); 
  24. N.GetSum(); 
  25. Myclass::GetSum(); 

關於靜態成員函數,可以總結爲以下幾點:

(1). 出現在類體外的函數定義不能指定關鍵字static


(2). static成員之間可以相互訪問 ,包括static成員函數訪問static數據成員和訪問static成員函數;


(3). 非靜態成員函數可以任意地訪問靜態成員函數和靜態數據成員;


(4). 靜態成員函數不能訪問非靜態成員函數和非靜態數據成員,只能訪問靜態的


(5). 由於沒有this指針的額外開銷,因此靜態成員函數與類的全局函數相比速度上會有少許的增長;


(6). 調用靜態成員函數,可以用成員訪問操作符(.)和(->)爲一個類的對象或指向類對象的指針調用靜態成員函數,也可以直接使用如下格式:
<類名>::<靜態成員函數名>(<參數表>)
如:Myclass::GetSum(),調用類的靜態成員函數。
但是,一樣要遵從public,protected,private訪問規則

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