1. 程式人生 > >各類變量生存周期及存儲空間

各類變量生存周期及存儲空間

應用程序 而是 hello func ack jpg 分享 style 應用

一、運行時環境簡介

程序在執行期間,將在其自己的邏輯地址空間內運行,其中每個程序值都在這個空間內有一個地址。一種典型的程序空間模式如下圖:

技術分享圖片

首先,運行時的數據包含數據區和代碼區。圖中的Text就是代碼區,存儲目標代碼。數據區包括圖中的Data、BSS、Heap和Stack。

1.(常量區)Data區主要存儲常量數據,主要是需要較多空間的常量,如 char s[32]=“Hello World”;中的“Hello World”。

2.(全局區)BSS主要存儲全局和靜態變量。

3.(堆區)Heap是動態存儲空間,主要用於程序在運行過程中動態申請存儲空間。

4.(棧區)Stack為運行時的函數提供存儲空間,局部變量就在此空間中。當然,除了局部變量,stack中還存放實現函數調用所必需的多種數據。

為了更有效地利用存儲空間,將Heap和Stack采取相向放置,每個區域向對方增長的形式。在具體實現時,棧向較低地址方向增長,而堆向較高地址方向增長。

二、局部變量和全局變量

在函數內部定義的變量將會分配在棧上,當函數被調用時,該變量才會被分配;當函數返回後,該變量就將被釋放,因此,局部變量僅僅在該函數內部範圍有效,只能在該函數內使用,因此稱為“局部變量”,也稱為“內部變量”。

局部變量的生存周期為函數的一次調用周期。

說明:

不同函數可以使用相同名字的局部變量,相互之間不會發生指代不明的情況,main函數雖然比較特殊,但是在局部變量的使用特點上與其他函數沒有區別。最後,用於接收或存儲參數的形式參數也被視為局部變量,同樣可以按照局部變量的方式使用。

在函數外部定義的變量將會分配在BSS區,其有效與否與函數調用過程無關,因此,其他函數均可以訪問此類變量,稱為“全局變量”。

1)全局變量的作用範圍:全局變量可被本文件中的其他函數訪問,其有效範圍是從定義變量的位置開始到該文件結束。在此有效範圍內的所有函數都可以訪問此變量,從而可以將全局變量作為函數之間交換信息的通道。

2)全局變量的生存周期:全局變量在程序執行過程中始終占用存儲單元,即全局變量的生存周期為程序的生存周期。因此,若大量使用全局變量,可能使得程序占據存儲單元過多,從而降低其他程序的可用儲存空間。

三、動態存儲和靜態存儲

局部變量和全局變量主要是從存儲空間角度對變量加以歸類,從變量的生存周期來看,可以分為靜態存儲和動態存儲。

靜態存儲方式是指變量的空間分配不以程序運行狀況決定,而是程序運行期間固定分配的方式。

動態存儲方式下,變量的分配將根據程序運行情況(如函數調用、動態內存分配等)來進行。如函數調用發生時分配變量空間,函數調用結束時釋放變量空間。

下面通過例子來理解變量生存期。

例1:(全局變量和局部變量生存周期)

int global_a;
int local_1()
{
    char local_1_c;
    local_2();
}

int local_2()
{
    char local_2_c;
}
int main()
{
    int main_i;
    for(main_i=0;main_i<3;main_i++)
    {
        local_1();
    }
    return 0;
}

1)假定t=1時程序啟動,全局變量global_a就開始存在,直到程序結束;main函數中的main_i也開始存在,直到退出main函數。

2)t=2時,main函數調用local_1函數時,local_1_c變量被分配空間,形成3個變量同時存活情況。

3)t=3時,local_1函數調用local_2函數,因為local_2_c變量也會被分配,形成4個變量在程序中共存的情況。

4)t=4時,local_2函數運行結束,local_2_c變量將被釋放,導致t=5時只有local_1_c、main_i和global_a共存的狀況。

5)t=5時,local_1函數運行結束,因此local_1_c變量將被釋放。導致t=5時只有main_i和global_a共存的狀況。

此後情況與先前類似。

從上例可以看到,C程序從空間作用域出發,對全局變量采取默認靜態分配策略,而對局部變量采取默認動態分配策略。C程序還提供了顯式指出存儲類別的變量分配方法。在C程序中,可以通過關鍵字auto和static來指定存儲類別。auto類別的變量將動態地分配存儲空間,數據儲存在動態存儲去中,如函數的形式參數和局部變量的缺省分配。而static類別的變量將存儲在靜態數據區,在程序執行期間一直存活。

void auto_static();
int main()
{
    int i;
    for (i=0;i<5;i++)
    {
        auto_static();
    }
    printf("\n");
    exit(0);
}

void auto_static()
{
    int var_auto=0;
    static int var_static=0;
    printf("var_auto is %d--",var_auto++);
    printf("var_static is %d\n",var_static++);
}

程序運行結果:

技術分享圖片

函數auto_static中,定義說明語句int var_auto=0;表明var_auto是局部變量,由於沒有指定存儲類別,所以默認采用動態存儲方式。

定義說明語句

static int var_static=0;

表明var_static是局部變量,同時指定存儲類別為static,所以將采取靜態存儲方式,var_static將在程序開始後分配內存空間,而無須等待auto_static()函數調用的發生。

技術分享圖片

技術分享圖片

四、內部函數和外部函數

當一個程序由多個源文件組成時,可以指定一個文件內的函數能被其他文件調用,也可以指定該函數只能被本文件使用。從這個意義上說,函數可以分為外部函數和內部函數,並通過指定函數類別限定符限制函數的相互調用。

函數在本質上是外部的,函數名同變量名一樣遵循作用域規則。C語言不允許函數嵌套定義,各個函數之間是平行並列的關系,相互之間可以調用,因而函數具有全局的有效範圍。但每個函數定義都附屬於某個程序文件,類似於變量。我們可以決定該函數的可被調用範圍是所有程序文件還是僅限於該函數定義所處的文件,即指定該函數是外部函數還是內部函數。

1.外部函數

在定義函數時,如果冠以關鍵字extern,則表示該函數是一個外部函數,例如:

extern double func1(double x){……}

這樣函數func1可被調用範圍(作用域)是所有程序文件,即它可以被任何其他函數所調用而不論這個函數是否與函數func1()處於同一個程序文件中。C語言規定,如果在定義時省略extern關鍵字,則默認為外部函數。

2.內部函數

在定義函數時,如果冠以關鍵字static,則表示該函數是一個內部函數,例如:

static double func1(double x){……}

這樣函數func1可被調用範圍(作用域)僅限於該函數定義所處的文件,即該函數被限制為僅能被本程序文件中的函數所調用。內部函數又稱靜態函數,使用內部函數時,有雨函數的調用僅局限於所在的文件,因而在不同文件中可以定義相同名稱的內部函數而相互不幹擾,這樣方便大型應用程序的編寫工作。

各類變量生存周期及存儲空間