存儲類型auto,static,extern,register的區別

變量和函數的屬性包括數據類型和數據的存儲類別,存儲類別指數據在內存中存儲方式(靜態和動態),包含auto,static,register,extern四種

內存中。具體點來說內存分爲三塊:靜態區,堆區,棧區。外部變量和全局變量存放在靜態區,局部變量存放在棧區,動態開闢的內存存在堆區。 

一、簡要性比較

extern   外部變量聲明,是指這是一個已在別的地方定義過的對象,這裏只是對變量的一次重複引用,不會產生新的變量。 
static   靜態數據,數據存放在全局數據區,但作用域只是本 文件/函數 中,所以你可以在兩個不同的文件/函數內部申明同名的static變量,但是           它們是兩個不同的全局變量。 如果是定義在函數內,那麼該對象具有無鏈接,函數外不能對其訪問。如果是定義在函數外,那麼該對象具          有內部鏈接,其它程序文件不能對其訪問
auto     普通局部棧變量,是自動存儲,這種對象會自動創建和銷燬 ,建議這個變量要放在堆棧上面,調用函數時分配內存,函數結束時釋放內            存。一般隱藏auto默認爲自動存儲類別。我們程序都變量大多是自動變量。
register 寄存器變量,
請求編譯器將這個變量保存在CPU的寄存器中,從而加快程序的運行.

          系統的寄存器是有限制的,聲明變量時如:register int i.這種存儲類型可以用於頻繁使用的變量

二、補充注意點

1、auto存儲類型的變量只能在某個程序範圍內使用,他採用堆棧的方式分配空間。

   在定義變量時,auto是可以省略的,如:auto   int   i;等效於int   i.

2、全局變量不允許聲明爲auto變量,register不適用於全局變量。

3、register是不能取址的。比如 int i;(自動爲auto)int *p=&i;是對的, 但register int j; int *p = &j;是錯的,因爲無法對寄存器的定址。

4.聲明變量爲register,編譯器並不一定會將它處理爲寄存器變量,畢竟,資源是有限的。

5、實際上現在一般的編譯器都忽略auto和register申明,現在的編譯器自己能夠區分最好將那些變量放置在寄存器中,那些放置在堆棧中;甚至於將一些變量有時存放在堆棧,有時存放在寄存器中。

6.auto register 是用來修飾變量的,static extern 變量函數都可以

三、詳細說明

從變量值存在時間(生存期)角度分:靜態存儲方式和動態存儲方式。

從變量的作用域(空間)角度分:全局變量和局部變量。

在用戶區的存儲空間:

|程序區    |

|靜態存儲區|

|動態存儲器|

靜態存儲方式程序運行期間系統分配固定存儲空間,動態存儲方式根據需求動態分配存儲空間。全局變量都存放於靜態存儲區,動態存儲區主要包括:函數的形參,在函數調用時分配存儲空間;自動變量(未加static聲明的局部變量);函數調用時的現場保護和返回地址等。

Auto自動變量:一般隱藏auto默認爲自動存儲類別。我們程序都變量大多是自動變量。

Static變量:static聲明的局部變量在函數調用結束後不釋放存儲空間,再次調用函數時該變量已經有值。其他函數時不能引用它的。Static局部變量時在編譯賦初值,自動變量的初值是函數調用時賦的。所以局部變量不賦初始值的話static變量自動賦值0或在空字符。而自動變量的初值則是不確定的。最好不要直接使用。函數中需要保留上次調用時的值或者初始化之後變量只被引用而不改變的可以考慮用static局部變量,這樣比較耗內存。

Register變量:動態和靜態變量都是存放在內存中,程序中遇到該值時用控制器發指令將變量的值送到運算器中,需要存數再保存到內存中。如果頻繁使用一個變量,比如一個函數體內的多次循環每次都引用該局部變量,我們則可以把局部變量的值放到CPU的寄存器中,叫寄存器變量。不需要多次到內存中存取提高效率。但是隻能局部自動變量和形參可以做寄存器變量。在函數調用時佔用一些寄存器,函數結束時釋放。不同系統對register要求也不一樣,比如對定義register變量個數,數據類型等限制,有的默認爲自動變量處理。所以在程序一般也不用。

Extern外部變量:如果我們希望該外部變量只能在本文件內使用,而不能被其他文件引用可以在外部變量定義時加static聲明。防止別人寫的模塊誤用。在函數外部定義的全局變量,作用域開始於變量定義,結束於程序文件的結束。我們可以extern來聲明外部變量來擴展它的作用域。同一個文件內,extern聲明之後就可以作用域擴大到聲明處到文件結束。比如在一個函數之後定義外部變量a,之後的函數可以使用該變量,但是之前的函數不能使用,加extern可以解決。多個文件時,可以在未定義該外部變量的文件內做extern聲明即可以使用。但是需要注意可能執行一個文件時改變了該全局變量的值,影響其他文件的調用。編譯時遇到extern,會先在文件內找是否定義了該外部變量。如果未找到則在鏈接時在其他文件中找。

四、示例

1、extern:

 extern的對象都具有靜態生命週期。使用extern時,注意不能重複定義,否則編譯報錯,如:
程序文件一:
extern int a = 10; //編譯警告,extern的變量最好不要初始化
程序文件二:
extern int a = 20; //重複定義,應改爲extern int a;

一般最好這樣,如果需要初始化,可把extern修飾符去掉(但也不要重複定義),另外如果其它程序文件也需要用到該變量,可用extern來聲明該變量。
2.
1. 局部變量: 也叫自動變量, 它聲明在函數開始, 生存於棧, 它的生命隨着函數返回而結束.
#include <stdio.h>

int main(void)
{
    auto int i = 9; /* 聲明局部變量的關鍵字是 auto; 因可以省略, 幾乎沒人使用 */   
    printf("%d\n", i);  
    getchar();
    return 0;
}



2. 全局變量: 聲明在函數體外, 一般應在函數前; 每個函數都可以使用它, 不過全局變量應儘量少用.
#include <stdio.h>

void add(void);
void mul(void);
int gi = 3; /* 全局變量 */
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; /* 全局變量 */
int main(void)
{
    int i; /* 句柄變量 */
    
    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); /* 123,222*/
    getchar();    
    return 0;
}

3. 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;        /* 一般的局部變量 */
    printf("%d\n", n++);
}
void fun2(void) {
    static int n;     /* 靜態局部變量; 會被初始化爲空 */
    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;}

4. 寄存器變量(register):寄存器變量會盡量把變量放到寄存器(而不是棧或堆), 從而加快存取速度
#include <stdio.h>
#include <time.h>

#define TIME 1000000000

int m, n = TIME; /* 全局變量 */
int main(void)
{   
    time_t start, stop;
    register int a, b = TIME; /* 寄存器變量 */
    int x, y = TIME;          /* 一般變量   */
    
    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;
}



4. extern 關鍵字:
使用外部全局變量應該用 extern 重新聲明一下;
如果不使用 extern, 將是重新定義;
在鏈接階段, 同一程序中不同單元的全局變量是共用的所以不能重新定義;
本例爲了省事並沒有涉及另一個文件, 只是把變量放在文件尾模擬了一下.

另外, extern 主要還是用於函數.
#include <stdio.h>

extern int g1;
int main(void)
{    
    extern int g2;
    printf("%d,%d\n", g1,g2);  
    getchar();
    return 0;
}
int g1 = 77;
int g2 = 88;

5. volatile 關鍵字:

程序在使用變量時, 特別是連續多次使用變量時, 一般是載入寄存器, 直接從寄存器存取, 之後再還回內存;
但如果此變量在返回內存時, 假如內存中的值已經改變了(從外部修改了)怎麼辦?
爲了避免這種情況的發生, 可以用 volatile 說明此變量, 以保證變量的每次使用都是直接從內存存取.
但這樣肯定會影響效率, 幸好它並不常用.

另外: 如果 const volatile 同時使用, 這表示此變量只接受外部的修改.
#include <stdio.h>

volatile int num = 123;
int main(void)
{    
    printf("%d\n", num);
    getchar();
    return 0;
}

6. restrict 關鍵字:

restrict 有點和 volatile 相反; 不過 restrict 只是對指針.
好像 C++Builder 2009 並不支持它, 暫時無法測試.
發佈了8 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章