【C】 39_程序中的三國天下(棧、堆、靜態存儲區)

程序中棧

  • 棧是現代計算機程序裏最爲重要的概念之一
  • 棧在程序中用於維護函數調用上下文
  • 函數中的參數和局部變量存儲在棧上

clipboard.png

  • 棧保存了一個函數調用所需的維護信息

clipboard.png

在程序中,棧是一種行爲,先進後出;
其它數據信息:函數調用時產生的臨時變量等;
ebp 指向函數調用後的返回地址
esp 棧頂指針。編程

函數調用過程

  • 每次函數調用都對應着棧上的活動記錄數組

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

clipboard.png

函數調用的棧變化 一

  • 從 main() 開始運行

clipboard.png

函數調用的棧變化 二

當 main() 調用 f()

clipboard.png

函數調用的棧變化 三

  • 當從 f() 調用中返回 main()

clipboard.png

ebp 指針向前讀 4 個字節,即 esp 指針所須要的返回地址;
ebp 指針向後讀 4 個字節,即 ebp 指針以前所指向的地址, ebp 返回。函數

函數調用棧的數據

  • 函數調用時,對應的棧空間在函數返回前是專用的
  • 函數調用結束後,棧空間被釋放,數據再也不有效

clipboard.png

編程實驗:指向棧數據的指針

Test_1.cspa

#include <stdio.h>

int* g()
{
    int a[10] = {0};
    
    return a;
}

void f()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int i = 0;

    int* pointer = g();
    
    for(i=0; i<10; i++)  
    {
        a[i] = pointer[i];
    }
    
    for(i=0; i<10; i++)
    {
        printf("%d\n", a[i]);
    }
}

int main()
{
    f();
    
    return 0;
}
編譯輸出:
main.c: In function ‘g’:
main.c:7: warning: function returns address of local variable

運行輸出:
0
0
0
0
0
0
0
0
0
0

分析:
g() 函數返回時, pointer 指向的棧空間的int[10]並無當即發生改變,輸出原始值。

Test_2.c指針

#include <stdio.h>

int* g()
{
    int a[10] = {0};
    
    return a;
}

void f()
{
    int i = 0;

    int* pointer = g();
    
    for(i=0; i<10; i++)
    {
        printf("%d\n", pointer[i]);
    }
}

int main()
{
    f();
    
    return 0;
}
編譯輸出:
main.c: In function ‘g’:
main.c:7: warning: function returns address of local variable

運行輸出:
0
16117748
0
0
-1077848536
14998560
-1077848488
14998560
16119008
134513936

分析:
pointer 指向的棧空間的int[10]發生改變,輸出非原始值。

兩次輸出結果不一樣,發生了什麼?code

g() 函數返回時,所對應活動記錄被釋放,但此時並無發生函數調用,所以數據沒有發生改變。
當調用 printf 函數時,printf 函數須要在棧上創建對應的活動記錄,g() 以前的活動記錄所以被改變,因此指針指向的內存空間值發生改變。pointer 指向的內存空間不在有意義,成爲野指針。對象

不能返回局部變量的地址和局部數組的數組名!生命週期

程序中的堆

  • 堆是程序中一塊預留的內存空間,可由程序自由使用
  • 堆被程序申請使用的內存在被主動釋放前一直有效

爲何有了棧還須要堆?ip

棧上的數據在函數返回後就會被釋放掉,沒法傳遞到函數外部,如:局部數組內存

  • C 語言程序中經過庫函數調用得到堆空間

    • 頭文件: malloc.h
    • malloc 以字節的方式動態申請堆空間
    • free 將堆空間歸還給系統
  • 系統對堆空間的管理方式

    • 空閒鏈表法,位圖法,對象池法等

clipboard.png

分析:

  1. 每一個節點下的內存都爲 12byte、100byte、50byte....
  2. 當申請內存時,系統遍歷空閒鏈表節點,查看所需內存大小與哪個節點下內存大小最接近
  3. 到此節點下查找可用單元,查找到以後返回對應單元地址

在空閒鏈表管理法中,存在查找所需內存大小與哪一節點最接近的操做,這將致使 malloc 實際分配的內存可能會比請求的多。但咱們不能依賴這種行爲,由於在不一樣的系統中,對堆空間的管理方式多是不一樣的。

程序中的靜態存儲區

  • 靜態存儲區隨着程序的運行而分配空間
  • 靜態存儲區的生命週期直到程序運行結束
  • 在程序的編譯期靜態存儲區的大小就已經肯定
  • 靜態存儲區主要用於保存全局變量和靜態變量
  • 靜態存儲區的信息(大小信息...)最終會保存待可執行程序中

編程實驗: 靜態存儲區的驗證

#include <stdio.h>

int g_v = 1;

static g_vs = 2;

void f()
{
    static int g_v1 = 3;
    
    printf("%p\n", &g_v1);
}

int main()
{
    printf("%p\n", &g_v);
    
    printf("%p\n", &g_vs);
    
    f();
    
    return 0;
}
輸出:
0x804a014
0x804a018
0x804a01c

分析:
在不一樣地方定義的全局變量、靜態局部變量順序排放在一塊兒;
靜態存儲區的大小及位置在編譯期就被肯定。

小結

  • 棧,堆和靜態存儲區是程序中的三個基本數據區

    • 棧主要用於函數調用的使用
    • 堆主要用於內存的動態申請和歸還
    • 靜態存儲區用於保存全局變量和靜態變量

以上內容參考狄泰軟件學院系列課程,請你們保護原創!

相關文章
相關標籤/搜索