您好,登錄后才能下訂單哦!
今天我們來探討下 C 語言中的數(shù)據(jù)存儲(chǔ)方式。在程序中,數(shù)據(jù)的存儲(chǔ)方式無外乎分為棧、堆以及靜態(tài)存儲(chǔ)區(qū)。我們分別來看看這三種方式,看看有何區(qū)別。
A、程序中的棧
棧是現(xiàn)代計(jì)算機(jī)程序里最為重要的概念之一,棧是用于維護(hù)函數(shù)調(diào)用上下文,同樣函數(shù)中的參數(shù)和局部變量存儲(chǔ)在棧上。棧保存了一個(gè)函數(shù)調(diào)用所需的維護(hù)信息,如下圖所示
那么每次函數(shù)調(diào)用都對(duì)應(yīng)著一個(gè)棧上的活動(dòng)記錄:a> 調(diào)用函數(shù)的活動(dòng)記錄位于棧的中部;b> 被調(diào)函數(shù)的活動(dòng)記錄位于棧的頂部。
從 main() 開始運(yùn)行,我們看到有兩個(gè)指針 ebp 和 esp。
那么當(dāng) main() 調(diào)用 f() 時(shí),ebp 就往前走四個(gè)字節(jié),指向原來 esp 的位置。esp 也繼續(xù)向前走
當(dāng)從 f() 調(diào)用總返回 main() 時(shí),ebp 和 esp 都向回退四個(gè)字節(jié)。
那么我們看到函數(shù)調(diào)用時(shí),對(duì)應(yīng)的??臻g在函數(shù)返回前是專用的。當(dāng)函數(shù)調(diào)用后??臻g將被釋放,數(shù)據(jù)不再有效。下圖更形象的表示了
下來我們以代碼為例進(jìn)行分析
#include <stdio.h> int* g() { int a[10] = {0}; return a; } void f() { int* pointer = g(); } int main() { f(); return 0; }
我們看到在函數(shù) g() 中定義了一個(gè)數(shù)組,但是我們返回了它的地址,也就是返回了局部數(shù)組的地址。在 f() 中調(diào)用了 g(),這樣肯定會(huì)出問題,我們來看看編譯結(jié)果
我們看到編譯器已經(jīng)給出了警告,那么這么操作肯定是不安全的。
B、程序中的堆
那么什么是堆呢?堆是程序中一塊預(yù)留的內(nèi)存空間,可由程序自由使用,堆中被程序申請(qǐng)使用的內(nèi)存在被主動(dòng)釋放前將一直有效。那么我們?yōu)槭裁从辛藯_€需要堆呢?棧上的數(shù)據(jù)在函數(shù)返回后就會(huì)被釋放掉,無法傳遞到函數(shù)外部,如:局部數(shù)組。那么堆則不一樣,如果我們不去主動(dòng)釋放,它就一直有效,但是也就造成了一個(gè)問題,如果我們只申請(qǐng)不去釋放堆,到最后堆用完了程序便會(huì)崩潰。
那么我們?cè)诔绦蛑性趺磥砩暾?qǐng)堆呢?在 C 語言程序中通過庫函數(shù)的調(diào)用來獲得堆空間。對(duì)應(yīng)的頭文件是 malloc.h;malloc 是以字節(jié)的方式動(dòng)態(tài)申請(qǐng)堆空間;free 是將堆空間歸還給系統(tǒng)。系統(tǒng)對(duì)堆空間的管理方式有這么幾種:空閑鏈表法、位圖法以及對(duì)象池法等。
下圖是空閑鏈表管理法的示意圖
如果我們需要申請(qǐng) 4 字節(jié)的話,根據(jù)這個(gè)表來看,我們便會(huì)申請(qǐng)到跟它最匹配的,便是 5 字節(jié)了。所以有時(shí)我們申請(qǐng)的空間會(huì)比我們所需的大一點(diǎn)。
C、程序中的靜態(tài)存儲(chǔ)區(qū)
靜態(tài)存儲(chǔ)區(qū)是隨著程序的運(yùn)行而分配空間,它的生命周期直到程序運(yùn)行結(jié)束。在程序的編譯器靜態(tài)存儲(chǔ)區(qū)的大小就已經(jīng)確定,主要用于保存全局變量和靜態(tài)局部變量,它保存的信息最終會(huì)保存到可執(zhí)行程序中。
下來我們以代碼為例來進(jìn)行分析
#include <stdio.h> int g_v = 1; static int g_vs = 2; void f() { static int g_vl = 3; printf("&g_vl = %p\n", &g_vl); } int main() { printf("&g_v = %p\n", &g_v); printf("&g_vs = %p\n", &g_vs); f(); return 0; }
我們看到分別定義了三個(gè)變量,第3行是 int 型的全局變量,第5行是加 static 修飾的 int 型變量,第9行是加 static 修飾的函數(shù)內(nèi)的局部變量。我們分別來打印下三個(gè)變量的地址,看看他們有什么關(guān)系
那么我們看到雖然他們?nèi)齻€(gè)類型不同,但是地址是連續(xù)的,也就證明他們?nèi)齻€(gè)是分布在同一個(gè)數(shù)據(jù)區(qū)的,便是靜態(tài)存儲(chǔ)區(qū)啦。通過本節(jié)對(duì)棧、堆以及靜態(tài)存儲(chǔ)區(qū)的學(xué)習(xí),總結(jié)如下:1、棧區(qū)主要用于函數(shù)調(diào)用的使用;2、堆區(qū)主要是用于內(nèi)存的動(dòng)態(tài)申請(qǐng)和歸還;3、靜態(tài)存儲(chǔ)區(qū)用于保存全局變量和靜態(tài)變量。
歡迎大家一起來學(xué)習(xí) C 語言,可以加我QQ:243343083。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。