1. 程式人生 > 其它 >(C語言記憶體二十一)C語言變數的儲存類別和生存期

(C語言記憶體二十一)C語言變數的儲存類別和生存期

前言

我們知道,變數是有資料型別的,用以說明它佔用多大的記憶體空間,可以進行什麼樣的操作。

除了資料型別,變數還有一個屬性,稱為“儲存類別”。儲存類別就是變數在記憶體中的存放區域。在程序的地址空間中, 常量區、全域性資料區和棧區可以用來存放變數的值

常量區和全域性資料區的記憶體在程式啟動時就已經由作業系統分配好,佔用的空間固定,程式執行期間不再改變,程式執行結束後才由作業系統釋放;它可以存放 全域性變數、靜態變數、一般常量和字串常量

棧區的記憶體在程式執行期間由系統根據需要來分配(使用到變數才分配記憶體;如果定義了變數但沒有執行到該程式碼,也不會分配記憶體),佔用的空間實時改變,使用完畢後立即釋放,不必等到程式執行結束;它可以存放區域性變數、函式引數等。

我們可以通過C語言中的關鍵字來控制變數的存放區域。C語言共有 4 個關鍵字用來指明變數的儲存類別: 你的字型auto(自動的)、static(靜態的)、register(暫存器的)、extern(外部的)

知道了變數的儲存類別,就可以知道變數的生存期。通俗地講,生存期指的是在程式執行過程中,變數從建立到銷燬的一段時間,生存期的長短取決於變數的儲存類別,也就是它所在的記憶體區域。

本節我們只講解 auto、static 和 register 三個關鍵字,external 將在模組化開發中講解。

auto 變數

auto 是自動或預設的意思,很少用到,因為所有的變數預設就是 auto 的。也就是說,定義變數時加不加 auto 都一樣,所以一般把它省略,不必多次一舉。

例如:

int n = 10;

auto int n = 10;

的效果完全一樣。

static 變數

static 宣告的變數稱為靜態變數,不管它是全域性的還是區域性的,都儲存在靜態資料區(全域性變數本來就儲存在靜態資料區,即使不加 static)。

靜態資料區的資料在程式啟動時就會初始化,直到程式執行結束;對於程式碼塊中的靜態區域性變數,即使程式碼塊執行結束,也不會銷燬。

注意:靜態資料區的變數只能初始化(定義)一次,以後只能改變它的值,不能再被初始化,即使有這樣的語句,也無效。

請看下面的程式碼:

#include <stdio.h>
#include <stdlib.h>
int main ()
{
    int result, i;
    for(i = 1; i<=100; i++){
        result = sum(i);
    }
    printf("1+2+3+...+99+100 = %d\n", result);
    system("pause");
    return 0;
}
int sum(int n){
    // 也可以不賦初值 0,靜態資料區的變數預設初始化為 0
    static int result = 0;
    result += n;
    return result;
}

執行結果:
1+2+3+...+99+100 = 5050

我們在 sum() 中定義了一個靜態區域性變數 result,它儲存在靜態資料區,sum() 函式執行結束也不會銷燬,下次呼叫繼續有效。靜態資料區的變數只能初始化一次,第一次呼叫 sum() 時已經對 result 進行了初始化,所以再次呼叫時就不會初始化了,也就是說 static int result = 0; 語句無效。

靜態區域性變數雖然儲存在靜態資料區,但是它的作用域僅限於定義它的程式碼塊,sum() 中的 result 在函式外無效,與 main() 中的 result 不衝突,除了變數名一樣,沒有任何關係。

register 變數

一般情況下,變數的值是儲存在記憶體中的,CPU 每次使用資料都要從記憶體中讀取。如果有一些變數使用非常頻繁,從記憶體中讀取就會消耗很多時間,例如 for 迴圈中的增量控制:

int i;
for(i=0; i<1000; i++){
    // Some Code
}

執行這段程式碼,CPU 為了獲得 i,會讀取 1000 次記憶體。

為了解決這個問題,可以將使用頻繁的變數放在CPU的通用暫存器中,這樣使用該變數時就不必訪問記憶體,直接從暫存器中讀取,大大提高程式的執行效率。

不過暫存器的數量是有限的,通常是把使用最頻繁的變數定義為 register 的。

來看一個計算 π 的近似值的例子,求解的一個近似公式如下:

為了提高精度,迴圈的次數越多越好,可以將迴圈的增量控制定義為暫存器變數,如下所示:

#include <stdio.h>
#include <conio.h>
int main()
{
    register int i = 0;  // 暫存器變數
    double sign = 1.0, res = 0, ad = 1.0;
    for(i=1; i<=100000000; i++)
    {
        res += ad;
        sign=-sign;
        ad=sign/(2*i+1);
    }
    res *= 4;
    printf("pi is %f", res);
    getch();
    return 0;
}

執行結果:
pi is 3.141593

關於暫存器變數有以下事項需要注意:

  1. 為暫存器變數分配暫存器是動態完成的,因此,只有區域性變數和形式引數才能定義為暫存器變數。

  2. 區域性靜態變數不能定義為暫存器變數,因為一個變數只能宣告為一種儲存類別。

  3. 暫存器的長度一般和機器的字長一致,只有較短的型別如 int、char、short 等才適合定義為暫存器變數,諸如 double 等較大的型別,不推薦將其定義為暫存器型別。

  4. CPU的暫存器數目有限,即使定義了暫存器變數,編譯器可能並不真正為其分配暫存器,而是將其當做普通的auto變數來對待,為其分配棧記憶體。當然,有些優秀的編譯器,能自動識別使用頻繁的變數,如迴圈控制變數等,在有可用的暫存器時,即使沒有使用 register 關鍵字,也自動為其分配暫存器,無須由程式設計師來指定。