- 棧是現代計算機程序裏最爲重要的概念之一
- 棧在程序中用於維護函數調用上下文
- 函數中的參數和局部變量存儲在棧上
- 棧保存了一個函數調用所需的維護信息
在程序中,棧是一種行爲,先進後出;
其它數據信息:函數調用時產生的臨時變量等;
ebp 指向函數調用後的返回地址
esp 棧頂指針。編程
每次函數調用都對應着棧上的活動記錄數組
- 調用函數的活動記錄位於棧的中部
- 被調函數的活動記錄位於棧的頂部
- 從 main() 開始運行
當 main() 調用 f()
- 當從 f() 調用中返回 main()
ebp 指針向前讀 4 個字節,即 esp 指針所須要的返回地址;
ebp 指針向後讀 4 個字節,即 ebp 指針以前所指向的地址, ebp 返回。函數
- 函數調用時,對應的棧空間在函數返回前是專用的
- 函數調用結束後,棧空間被釋放,數據再也不有效
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 將堆空間歸還給系統
系統對堆空間的管理方式
- 空閒鏈表法,位圖法,對象池法等
分析:
在空閒鏈表管理法中,存在查找所需內存大小與哪一節點最接近的操做,這將致使 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 分析: 在不一樣地方定義的全局變量、靜態局部變量順序排放在一塊兒; 靜態存儲區的大小及位置在編譯期就被肯定。
棧,堆和靜態存儲區是程序中的三個基本數據區
- 棧主要用於函數調用的使用
- 堆主要用於內存的動態申請和歸還
- 靜態存儲區用於保存全局變量和靜態變量
以上內容參考狄泰軟件學院系列課程,請你們保護原創!