C/C++中存儲類型修飾符的區別(auto、static、register、extern、volatile、restrict)

一、局部變量和全局變量:

(1)局部變量:局部變量也叫自動變量,它聲明在函數開始,生存於棧,它的生命隨着函數的返回而結束。

  1. #include <stdio.h>  
  2.   
  3. int main(void)  
  4. {  
  5.     auto int i = 9; <span style="white-space:pre">  </span>//聲明局部變量的關鍵字是 auto; 因可以省略,所以幾乎沒人使用  
  6.   
  7.     printf("%d\n", i);    
  8.     getchar();  
  9.     return 0;  
  10. }  
  1.   

(2)全局變量:全局變量聲明在函數體外,一般應在函數前。每個函數都可以使用它,不過全局變量應儘量少用。

  1. #include <stdio.h>  
  2.   
  3. void add(void);  
  4. void mul(void);  
  5.   
  6. int gi = 3; <span style="white-space:pre">      </span>//全局變量(聲明在函數外)  
  7.   
  8. int main(void)  
  9. {      
  10.     printf("%d\n", gi); //輸出的是 3  
  11.   
  12.     add();  
  13.     printf("%d\n", gi); //輸出的是 5  
  14.   
  15.     mul();  
  16.     printf("%d\n", gi); //輸出的是 10  
  17.         
  18.     getchar();  
  19.     return 0;  
  20. }  
  21.   
  22. void add(void) {  
  23.     gi += 2;  
  24. }  
  25.   
  26. void mul(void) {  
  27.     gi *= 2;  
  28. }  
全局變量會被初始化爲空, 而局部變量在沒有賦值前是一個垃圾值:

  1. #include <stdio.h>  
  2.   
  3. int gi;<span style="white-space:pre">           </span>//全局變量  
  4.   
  5. int main(void)  
  6. {  
  7.     int i;<span style="white-space:pre">        </span>//局部變量  
  8.       
  9.     printf("%d, %d\n", gi, i);  
  10.         
  11.     getchar();  
  12.     return 0;  
  13. }  
當全局變量與局部變量重名時,使用的是局部變量:

  1. #include <stdio.h>  
  2.   
  3. int a = 111, b = 222;  
  4.   
  5. int main(void)  
  6. {  
  7.     int a = 123;  
  8.     printf("%d,%d\n", a, b);<span style="white-space:pre">  </span>//輸出的是 123,222  
  9.   
  10.     getchar();      
  11.     return 0;  
  12. }  


二、對象的生存週期(lifetime)

(1)靜態生存週期(即全局變量的生存週期)

具有靜態生存週期的所有對象,都是在程序開始執行之前就被事先創建和初始化。它們的壽命覆蓋整個程序的執行過程。如在函數內定義了一個static變量,那第一次調用該函數後,該變量的值將會被保留,當第二次被調用時,該變量的值還是第一次調用結束時的值。

(2)自動生存週期(即局部變量的生存週期)

自動生存週期的對象的壽命由“對象定義所處在的大括號{}”決定。每次程序執行流進入一個語句塊,此語句塊自動生存週期的對象就會被創建一個新實例,同時被初始化。


三、標識符的鏈接(linkage)

(1)外部鏈接

表示在整個程序中(多個程序文件)是相同的函數或對象。常見的有,在函數體外聲明的extern變量。

(2)內部鏈接

表示只在當前程序文件中是相同的函數或對象。其它程序文件不能對其進行訪問。常見的有,在函數體外聲明的static變量。

(3)無鏈接

一般聲明在函數內部的auto、register變量、還有函數的參數,都是無鏈接。它的作用域是函數內部。


四、存儲類型修飾符總結:

存儲類型修飾符可以修改標識符的鏈接和對應對象的生存週期;標識符有鏈接,而非生命週期;對象有生存週期,而非鏈接;函數標識符只可用static、extern修飾,函數參數只可用register修飾。


(1)auto(對應自動生存週期)

auto修飾符只能用在函數內的對象聲明中,即僅在語句塊內使用。

聲明中有auto修飾符的對象具有自動生存週期。

它們僅存在於被定義的當前執行代碼塊中,即局部變量在進入模塊時生成,在退出模塊時消亡。

定義局部變量的最常見的代碼塊是函數。 語言中包括了關鍵字auto,它可用於定義局部變量。但自從所有的非全局變量的缺省值假定爲auto以來,auto就幾乎很少使用了。


(2)static(對應靜態生存週期)

如果是定義在函數外,那麼該對象具有內部鏈接,其它程序文件不能對其訪問。如果是定義在函數內,那麼該對象具有無鏈接,函數外不能對其訪問。

(注意:static變量初始化時,只能用常量)

用 static 關鍵字修飾的局部變量稱爲靜態局部變量。

靜態局部變量存值如同全局變量,區別在於它只屬於擁有它的函數,它也和全局變量一樣會被初始化爲空。

  1. #include <stdio.h>  
  2.   
  3. void fun1(void);  
  4. void fun2(void);  
  5.   
  6. int main(void)  
  7. {      
  8.     int i;  
  9.       
  10.     for (i = 0; i < 10; i++) fun1();  
  11.     printf("---\n");  
  12.     for (i = 0; i < 10; i++) fun2();  
  13.       
  14.     getchar();  
  15.     return 0;  
  16. }  
  17.   
  18. void fun1(void) {  
  19.     int n = 0;<span style="white-space:pre">            </span>//一般的局部變量  
  20.     printf("%d\n", n++);  
  21. }  
  22.   
  23. void fun2(void) {  
  24.     static int n;<span style="white-space:pre">     </span>//靜態局部變量,會被初始化爲空  
  25.     printf("%d\n", n++);  
  26. }  

用 static 關鍵字修飾的全局變量是靜態全局變量,靜態全局變量只能用於定義它的單元。

  1. //譬如在 File1.c 裏面定義了:  
  2. static int num = 99;  /* 去掉前面的 static 其他單元纔可以使用 */  
  3.   
  4. //在 File2.c 裏使用:  
  5. #include <stdio.h>  
  6.   
  7. extern int num;  
  8.   
  9. int main(void)  
  10. {      
  11.     printf("%d\n", num);  
  12.    
  13.     getchar();  
  14.     return 0;  
  15. }  



用靜態變量記錄函數被調用的次數:

  1. #include <stdio.h>  
  2.   
  3. int fun(void);  
  4.   
  5. int main(void)  
  6. {      
  7.     int i;  
  8.     for (i = 0; i < 10; i++) {  
  9.         printf("函數被調用了 %2d 次;\n", fun());  
  10.     }       
  11.     getchar();  
  12.     return 0;  
  13. }  
  14.   
  15. int fun(void) {  
  16.     static int n;  
  17.     return ++n;  
  18. }  

(3)const


(4)extern(對應靜態生存週期)

extern 意爲“外來的”。它的作用在於告訴編譯器:這個變量或者函數的定義在別的地方,當遇到此變量或函數時應到其他模塊中尋找其定義。

(PS:這個變量,它可能不存在於當前的文件中,但它肯定要存在於工程中的某一個源文件中或者一個Dll的輸出中。)

  1. #include <stdio.h>  
  2.   
  3. extern int g1;  
  4.   
  5. int main(void)  
  6. {      
  7.     extern int g2;<span style="white-space:pre">    </span>//告訴編譯器g2定義在其他地方  
  8.   
  9.     printf("%d,%d\n", g1,g2);    
  10.     getchar();  
  11.     return 0;  
  12. }  
  13.   
  14. int g1 = 77;  
  15. int g2 = 88;  

使用extern時,注意不能重複定義,否則編譯報錯,如:

程序文件一:

  1. extern int a = 10; <span style="white-space:pre">   </span>//編譯警告,extern的變量最好不要初始化   

程序文件二:

  1. extern int a = 20; <span style="white-space:pre">   </span>//重複定義,應改爲extern int a;   

(一般最好這樣,如果需要初始化,可把extern修飾符去掉(但也不要重複定義),另外如果其它程序文件也需要用到該變量,可用extern來聲明該變量。這樣會比較清晰。)

另外,extern也可用來進行鏈接指定。


(5)volatile


(6)register(即寄存器變量,對應自動生存週期)

當聲明對象有自動生存週期時,可以使用register修飾符。因此,register也只能用在函數內的聲明中。

register修飾符暗示編譯程序相應的變量將被頻繁地使用,如果可能的話,應將其保存在CPU的寄存器中(而不是棧或堆),以加快其存儲速度。然而,編譯器不見得會這麼做,因此效果一般般。瞭解一下就行,不建議使用。

  1. #include <stdio.h>  
  2. #include <time.h>  
  3.   
  4. #define TIME 1000000000  
  5.   
  6. int m, n = TIME;<span style="white-space:pre">      </span>//全局變量  
  7.   
  8. int main(void)  
  9. {     
  10.     time_t start, stop;  
  11.   
  12.     register int a, b = TIME;<span style="white-space:pre"> </span>//寄存器變量  
  13.     int x, y = TIME;<span style="white-space:pre">      </span>//一般變量  
  14.       
  15.     time(&start);  
  16.     for (a = 0; a < b; a++);  
  17.     time(&stop);  
  18.     printf("寄存器變量用時: %d 秒\n", stop - start);  
  19.   
  20.     time(&start);  
  21.     for (x = 0; x < y; x++);  
  22.     time(&stop);  
  23.     printf("一般變量用時: %d 秒\n", stop - start);  
  24.   
  25.     time(&start);  
  26.     for (m = 0; m < n; m++);  
  27.     time(&stop);  
  28.     printf("全局變量用時: %d 秒\n", stop - start);  
  29.    
  30.     getchar();  
  31.     return 0;  
  32. }  

使用register修飾符有幾點限制:

  首先,register變量必須是能被CPU所接受的類型。這通常意味着register變量必須是一個單個的值,並且長度應該小於或者等於整型的長度。不過,有些機器的寄存器也能存放浮點數。

  其次,因爲register變量有可能被存放到寄存器中而不是內存中,所以不能用“&”來獲取register變量的地址。


  總的來說,由於寄存器的數量有限,而且某些寄存器只能接受特定類型的數據(如指針和浮點數),因此真正起作用的register修飾符的數目和類型都依賴於運行程序的機器,而任何多餘的register修飾符都將被編譯程序所忽略。

  在某些情況下,把變量保存在寄存器中反而會降低程序的運行速度。因爲被佔用的寄存器不能再用於其它目的;或者變量被使用的次數不夠多,不足以裝入和存儲變量所帶來的額外開銷。

  早期的C編譯程序不會把變量保存在寄存器中,除非你命令它這樣做,這時register修飾符是C語言的一種很有價值的補充。然而,隨着編譯程序設計技術的進步,在決定那些變量應該被存到寄存器中時,現在的C編譯環境能比程序員做出更好的決定。實際上,許多編譯程序都會忽略register修飾符,因爲儘管它完全合法,但它僅僅是暗示而不是命令。


(7)缺省修飾符

函數內,與auto相同,函數外,與extern相同。




五、概括性例子:

  1. int func1(void); <span style="white-space:pre"> </span>//func1具有外部鏈接  
  2. int a = 10; <span style="white-space:pre">      </span>//a具有外部鏈接,靜態生存週期      
  3. extern int b = 1; <span style="white-space:pre">    </span>//b具有外部鏈接,靜態生存週期。但編譯會有警告extern變量不應初始化,同時也要注意是否會重複定義  
  4. static int c; <span style="white-space:pre">        </span>//c具有內部鏈接,靜態生存週期  
  5. static int e; <span style="white-space:pre">        </span>//e具有內部鏈接,靜態生存週期  
  6. static void func2(int d)  
  7. {  
  8. <span style="white-space:pre">  </span>//func2具有內部鏈接;參數d具有無鏈接,自動生存週期  
  9. <span style="white-space:pre">  </span>extern int a; <span style="white-space:pre"> </span>//a與上面的a一樣(同一變量),具有外部鏈接,靜態生存週期。注意這裏的不會被默認初始爲0,它只是個聲明  
  10. <span style="white-space:pre">  </span>int b = 2; <span style="white-space:pre">    </span>//b具有無鏈接,自動生存同期。並且將上面聲明的b隱藏起來  
  11. <span style="white-space:pre">  </span>extern int c; <span style="white-space:pre"> </span>//c與上面的c一樣,維持內部鏈接,靜態生存週期。注意這裏的不會被默認初始爲0,它只是個聲明  
  12. <span style="white-space:pre">  </span>//如果去掉了extern修飾符,就跟b類似了,無鏈接,自動生存週期,把上面聲明的c隱藏起來  
  13. <span style="white-space:pre">  </span>static int e; <span style="white-space:pre"> </span>//e具有無鏈接,靜態生存週期。並且將上面聲明的e隱藏起來;初始化值爲0  
  14. <span style="white-space:pre">  </span>static int f; <span style="white-space:pre"> </span>//f具有無鏈接,靜態生存週期  
  15. }    


相關鏈接參考:

http://developer.51cto.com/art/201105/261465.htm

http://apps.hi.baidu.com/share/detail/30353645

http://www.cnblogs.com/del/archive/2008/12/04/1347305.html

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