存儲類型修飾符可以修改標識符的鏈接和對應對象的生存周期;標識符有鏈接,而非生命周期;對象有生存周期,而非鏈接;函數標識符只可用static、extern修飾,函數參數只可用register修飾。
一、標識符的鏈接linkage)
1)外部鏈接
表示在整個程序中多個程序文件)是相同的函數或對象。常見的有,在函數體外聲明的extern變量。
2)內部鏈接
表示只在當前程序文件中是相同的函數或對象。其它程序文件不能對其進行訪問。常見的有,在函數體外聲明的static變量。
3)無鏈接
一般聲明在函數內部的auto、register變量、還有函數的參數,都是無鏈接。它的作用域是函數內部。
二、對象的生存周期lifetime)
1)靜態生存周期
具有靜態生存周期的所有對象,都是在程序開始執行之前就被事先創建和初始化。它們的壽命覆蓋整個程序的執行過程。如在函數內定義了一個static變量,那第一次調用該函數後,該變量的值將會被保留,當第二次被調用時,該變量的值還是第一次調用結束時的值。
2)自動生存周期
自動生存周期的對象的壽命由“對象定義所處在的大括號{}”決定。每次程序執行流進入一個語句塊,此語句塊自動生存周期的對象就會被創建一個新實例,同時被初始化。
三、存儲類修飾符
1)auto
auto修飾符只能用在函數內的對象聲明。聲明中有auto修飾符的對象具有自動生存周期。
在ANSI C中,函數內的對象聲明在默認情況下有自動生存周期,所以在函數內聲明時auto可省略。
2)register
當聲明對象有自動生存周期時,可以使用register修飾符。因此,register也只能用在函數內的聲明中。
此關鍵字告訴編譯器:此對象的存取應該盡量快,最好存儲在CPU的寄存器中。然而,編譯器不見得會這麼做。
另外要注意的是,當一個對象聲明為register,就不可使用地址運算符&了,因為它有可能被放到寄存器中。
3)static
函數標識符如果被聲明為static,就具有靜態生命周期。如果是定義在函數外,那麼該對象具有內部鏈接,其它程序文件不能對其訪問。如果是定義在函數內,那麼該對象具有無鏈接,函數外不能對其訪問。
注意:static變量初始化時,只能用常量。
4)extern
如果聲明在函數外,那麼該對象具有外部鏈接,能夠在其它程序文件使用。但要注意它有可能會被函數內定義的重名的變量所隱藏起來。
如果聲明在函數內,該對象具有何種鏈接取決於當前程序文件中定義在函數外的相同名字的對象。如果在函數外也定義了一下相同名字的static對象,則該函數內的對象具有無鏈接,否則具有外部鏈接。
extern的對象都具有靜態生命周期。
使用extern時,注意不能重復定義,否則編譯報錯,如:
程序文件一:
- extern int a = 10; //編譯警告,extern的變量最好不要初始化
程序文件二:
- extern int a = 20; //重復定義,應改為extern int a;
一般最好這樣,如果需要初始化,可把extern修飾符去掉但也不要重復定義),另外如果其它程序文件也需要用到該變量,可用extern來聲明該變量。這樣會比較清晰。
5)缺省修飾符
函數內,與auto相同;函數外,與extern相同。
例子:
- int func1(void); //func1具有外部鏈接;
- int a = 10; //a具有外部鏈接,靜態生存周期;
- extern int b = 1; //b具有外部鏈接,靜態生存周期。但編譯會有警告extern變量不應初始化,同時也要注意是否會重復定義;
- static int c; //c具有內部鏈接,靜態生存周期;
- static int e; //e具有內部鏈接,靜態生存周期;
- static void func2(int d){ //func2具有內部鏈接;參數d具有無鏈接,自動生存周期;
- extern int a; //a與上面的a一樣同一變量),具有外部鏈接,靜態生存周期。注意這裡的不會被默認初始為0,它只是個聲明;
- int b = 2; //b具有無鏈接,自動生存同期。並且將上面聲明的b隱藏起來;
- extern int c; //c與上面的c一樣,維持內部鏈接,靜態生存周期。注意這裡的不會被默認初始為0,它只是個聲明;
- //如果去掉了extern修飾符,就跟b類似了,無鏈接,自動生存周期,把上面聲明的c隱藏起來;
- static int e; //e具有無鏈接,靜態生存周期。並且將上面聲明的e隱藏起來;初始化值為0;
- static int f; //f具有無鏈接,靜態生存周期;
- }