首頁 > 軟體

C語言深入講解棧與堆和靜態儲存區的使用

2022-04-18 16:00:59

一、程式中的棧

  • 棧是現代計算機程式裡最為重要的概念之一
  • 棧在程式中用於維護函數呼叫上下文
  • 函數中的引數和區域性變數儲存在棧上

 棧儲存了一個函數呼叫所需的維護資訊

  • 引數
  • 返回地址
  • 區域性變數
  • 呼叫上下文

二、函數的呼叫過程

每次函數呼叫都對應著一個棧上的活動記錄

  • 呼叫函數的活動記錄位於棧的中部
  • 被調函數的活動記錄位於棧的頂部

三、函數呼叫的棧變化

從main() 開始執行

 main() 呼叫 f()

 當從 f() 呼叫中返回 main()

四、函數呼叫棧上的資料

  • 函數呼叫時,對應的棧空間在函數返回前是專用的
  • 函數呼叫結束後,棧空間將被釋放,資料不再有效

        下面看一個指向棧資料的指標: 

#include <stdio.h>
 
int* g()
{
    int a[10] = {0};
    return a;
}
 
void f()
{
    int i = 0;
    int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *pointer = g();
    
    for (i = 0; i < 10; i++)
    {
        b[i] = pointer[i];
    }
    for(i = 0; i < 10; i++)
    {
        printf("%dn", b[i]);
    } 
}
 
int main()
{
    f();
    return 0;
}

         輸出結果如下:

        如果把 

        for (i = 0; i < 10; i++)

       {

                b[i] = pointer[i];

       }

        註釋了,直接列印 pointer[i] 裡面的資料,如下:

#include <stdio.h>
 
int* g()
{
    int a[10] = {0};
    return a;
}
 
void f()
{
    int i = 0;
    int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *pointer = g();
    /*
    for (i = 0; i < 10; i++)
    {
        b[i] = pointer[i];
    }
    */
    for(i = 0; i < 10; i++)
    {
        printf("%dn", pointer[i]);
    } 
}
 
int main()
{
    f();
    return 0;
}

        輸出結果如下:

         為什麼直接列印 pointer[i] 裡面的值會是這樣呢?因為 pointer 指向的空間是棧空間,棧空間在 g() 函數返回之後,活動記錄就被釋放了。被釋放後呼叫 printf 函數,printf 函數需要在棧上面建立一個活動記錄。這個活動記錄就會有 printf 函數的引數資訊和返回值等,所以 pointer 所指向的記憶體裡面的資料由於 printf 函數的呼叫被改變了。因此,不能返回區域性變數的地址,不能返回區域性陣列的陣列名。

五、程式中的堆

  • 堆是程式中一塊預留的記憶體空間,可由程式自由使用
  • 堆中被程式申請使用的記憶體在被主動釋放前將一直有效

        為什麼有了棧還需要堆?

        答:棧上的資料在函數返回後就會被釋放掉,無法傳遞到函數外部,如:區域性陣列

C語言程式中通過庫函數的呼叫獲得堆空間

  • 標頭檔案:malloc.h
  • malloc -- 以位元組的方式動態申請堆空間
  • free -- 將堆空間歸還給系統

系統對堆空間的管理方式

空閒連結串列法,點陣圖法,物件池法等等

        以 int* p = (int*)malloc(sizeof(int)); 為例,要申請 4 個位元組的大小,遍歷之後發現跟 5 Bytes 這個節點最接近,找到一個可以用的單元之後,就將這個單元的地址返還給了 p 指標。以前也提過使用 malloc 申請記憶體空間時返回的記憶體空間可能比申請的實際記憶體空間要大一點點,原因就是在空閒連結串列管理堆空間這樣的系統裡面,它會找最近的那個,找到後的一般都大於等於所需要的記憶體空間,假如 5 Bytes 這個節點下所有的空閒記憶體單元都用完的話,就會找 12 Bytes 節點下的記憶體單元,這樣malloc 返回的記憶體空間就有可能比自己實際申請的記憶體空間要大。

六、程式中的靜態儲存區

  • 靜態儲存區隨著程式的執行而分配空間
  • 靜態儲存區的生命週期直到程式執行結束
  • 在程式的編譯期靜態儲存區的大小就已經確定
  • 靜態儲存區主要用於儲存全域性變數和靜態區域性變數
  • 靜態儲存區的資訊最終會儲存到可執行程式中

        下面看一個靜態儲存區的驗證程式碼:

#include <stdio.h>
 
int g_v = 1;
 
static int g_vs  = 2;
 
void f()
{
    static int g_vl = 3;
    
    printf("%pn", &g_vl);
}
 
int main()
{
    printf("%pn", &g_v);
    
    printf("%pn", &g_vs);
    
    f();
    
    return 0;
}

        輸出結果如下:

         可以看到這三個地址是順序存放的,因為這三個變數都是存放在程式的靜態儲存區,靜態儲存區在程式裡面有固定的起始地址。 

七、小結

棧,堆和靜態儲存區是程式中的三個基本資料區

  • 棧區主要用於函數呼叫的使用
  • 堆區主要是用於記憶體的動態申請和歸還
  • 靜態儲存區用於儲存全域性變數和靜態變數

到此這篇關於C語言深入講解棧與堆和靜態儲存區的使用的文章就介紹到這了,更多相關C語言 棧與堆內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com