一、局部變量和全局變量:
(1)局部變量:局部變量也叫自動變量,它聲明在函數開始,生存於棧,它的生命隨着函數的返回而結束。
- #include <stdio.h>
- int main(void)
- {
- auto int i = 9; <span style="white-space:pre"> </span>//聲明局部變量的關鍵字是 auto; 因可以省略,所以幾乎沒人使用
- printf("%d\n", i);
- getchar();
- return 0;
- }
(2)全局變量:全局變量聲明在函數體外,一般應在函數前。每個函數都可以使用它,不過全局變量應儘量少用。
- #include <stdio.h>
- void add(void);
- void mul(void);
- int gi = 3; <span style="white-space:pre"> </span>//全局變量(聲明在函數外)
- int main(void)
- {
- printf("%d\n", gi); //輸出的是 3
- add();
- printf("%d\n", gi); //輸出的是 5
- mul();
- printf("%d\n", gi); //輸出的是 10
- getchar();
- return 0;
- }
- void add(void) {
- gi += 2;
- }
- void mul(void) {
- gi *= 2;
- }
- #include <stdio.h>
- int gi;<span style="white-space:pre"> </span>//全局變量
- int main(void)
- {
- int i;<span style="white-space:pre"> </span>//局部變量
- printf("%d, %d\n", gi, i);
- getchar();
- return 0;
- }
- #include <stdio.h>
- int a = 111, b = 222;
- int main(void)
- {
- int a = 123;
- printf("%d,%d\n", a, b);<span style="white-space:pre"> </span>//輸出的是 123,222
- getchar();
- return 0;
- }
二、對象的生存週期(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 關鍵字修飾的局部變量稱爲靜態局部變量。
靜態局部變量存值如同全局變量,區別在於它只屬於擁有它的函數,它也和全局變量一樣會被初始化爲空。
- #include <stdio.h>
- void fun1(void);
- void fun2(void);
- int main(void)
- {
- int i;
- for (i = 0; i < 10; i++) fun1();
- printf("---\n");
- for (i = 0; i < 10; i++) fun2();
- getchar();
- return 0;
- }
- void fun1(void) {
- int n = 0;<span style="white-space:pre"> </span>//一般的局部變量
- printf("%d\n", n++);
- }
- void fun2(void) {
- static int n;<span style="white-space:pre"> </span>//靜態局部變量,會被初始化爲空
- printf("%d\n", n++);
- }
用 static 關鍵字修飾的全局變量是靜態全局變量,靜態全局變量只能用於定義它的單元。
- //譬如在 File1.c 裏面定義了:
- static int num = 99; /* 去掉前面的 static 其他單元纔可以使用 */
- //在 File2.c 裏使用:
- #include <stdio.h>
- extern int num;
- int main(void)
- {
- printf("%d\n", num);
- getchar();
- return 0;
- }
用靜態變量記錄函數被調用的次數:
- #include <stdio.h>
- int fun(void);
- int main(void)
- {
- int i;
- for (i = 0; i < 10; i++) {
- printf("函數被調用了 %2d 次;\n", fun());
- }
- getchar();
- return 0;
- }
- int fun(void) {
- static int n;
- return ++n;
- }
(3)const
(4)extern(對應靜態生存週期)
extern 意爲“外來的”。它的作用在於告訴編譯器:這個變量或者函數的定義在別的地方,當遇到此變量或函數時應到其他模塊中尋找其定義。
(PS:這個變量,它可能不存在於當前的文件中,但它肯定要存在於工程中的某一個源文件中或者一個Dll的輸出中。)
- #include <stdio.h>
- extern int g1;
- int main(void)
- {
- extern int g2;<span style="white-space:pre"> </span>//告訴編譯器g2定義在其他地方
- printf("%d,%d\n", g1,g2);
- getchar();
- return 0;
- }
- int g1 = 77;
- int g2 = 88;
使用extern時,注意不能重複定義,否則編譯報錯,如:
程序文件一:
- extern int a = 10; <span style="white-space:pre"> </span>//編譯警告,extern的變量最好不要初始化
程序文件二:
- extern int a = 20; <span style="white-space:pre"> </span>//重複定義,應改爲extern int a;
(一般最好這樣,如果需要初始化,可把extern修飾符去掉(但也不要重複定義),另外如果其它程序文件也需要用到該變量,可用extern來聲明該變量。這樣會比較清晰。)
另外,extern也可用來進行鏈接指定。
(5)volatile
(6)register(即寄存器變量,對應自動生存週期)
當聲明對象有自動生存週期時,可以使用register修飾符。因此,register也只能用在函數內的聲明中。
register修飾符暗示編譯程序相應的變量將被頻繁地使用,如果可能的話,應將其保存在CPU的寄存器中(而不是棧或堆),以加快其存儲速度。然而,編譯器不見得會這麼做,因此效果一般般。瞭解一下就行,不建議使用。
- #include <stdio.h>
- #include <time.h>
- #define TIME 1000000000
- int m, n = TIME;<span style="white-space:pre"> </span>//全局變量
- int main(void)
- {
- time_t start, stop;
- register int a, b = TIME;<span style="white-space:pre"> </span>//寄存器變量
- int x, y = TIME;<span style="white-space:pre"> </span>//一般變量
- time(&start);
- for (a = 0; a < b; a++);
- time(&stop);
- printf("寄存器變量用時: %d 秒\n", stop - start);
- time(&start);
- for (x = 0; x < y; x++);
- time(&stop);
- printf("一般變量用時: %d 秒\n", stop - start);
- time(&start);
- for (m = 0; m < n; m++);
- time(&stop);
- printf("全局變量用時: %d 秒\n", stop - start);
- getchar();
- return 0;
- }
使用register修飾符有幾點限制:
(7)缺省修飾符
函數內,與auto相同,函數外,與extern相同。
五、概括性例子:
- int func1(void); <span style="white-space:pre"> </span>//func1具有外部鏈接
- int a = 10; <span style="white-space:pre"> </span>//a具有外部鏈接,靜態生存週期
- extern int b = 1; <span style="white-space:pre"> </span>//b具有外部鏈接,靜態生存週期。但編譯會有警告extern變量不應初始化,同時也要注意是否會重複定義
- static int c; <span style="white-space:pre"> </span>//c具有內部鏈接,靜態生存週期
- static int e; <span style="white-space:pre"> </span>//e具有內部鏈接,靜態生存週期
- static void func2(int d)
- {
- <span style="white-space:pre"> </span>//func2具有內部鏈接;參數d具有無鏈接,自動生存週期
- <span style="white-space:pre"> </span>extern int a; <span style="white-space:pre"> </span>//a與上面的a一樣(同一變量),具有外部鏈接,靜態生存週期。注意這裏的不會被默認初始爲0,它只是個聲明
- <span style="white-space:pre"> </span>int b = 2; <span style="white-space:pre"> </span>//b具有無鏈接,自動生存同期。並且將上面聲明的b隱藏起來
- <span style="white-space:pre"> </span>extern int c; <span style="white-space:pre"> </span>//c與上面的c一樣,維持內部鏈接,靜態生存週期。注意這裏的不會被默認初始爲0,它只是個聲明
- <span style="white-space:pre"> </span>//如果去掉了extern修飾符,就跟b類似了,無鏈接,自動生存週期,把上面聲明的c隱藏起來
- <span style="white-space:pre"> </span>static int e; <span style="white-space:pre"> </span>//e具有無鏈接,靜態生存週期。並且將上面聲明的e隱藏起來;初始化值爲0
- <span style="white-space:pre"> </span>static int f; <span style="white-space:pre"> </span>//f具有無鏈接,靜態生存週期
- }
相關鏈接參考:
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