39_程序中的三國(guó)天下

0. 問(wèn)題:C語(yǔ)言中的數(shù)據(jù)存儲(chǔ)區(qū)有哪些?

1. 程序中的棧

棧是現(xiàn)代計(jì)算機(jī)程序里最為重要的概念狂魔,其作用是用于維護(hù)函數(shù)調(diào)用上下文,也就是說(shuō)在我們程序運(yùn)行的時(shí)候必須要使用的存儲(chǔ)區(qū)就是棧存儲(chǔ)區(qū),因?yàn)镃程序是一個(gè)接著一個(gè)的函數(shù)調(diào)用锅论,并且C程序是從main函數(shù)開(kāi)始,既然使用了main函數(shù)令宿,必然就是使用棧存儲(chǔ)區(qū)(因?yàn)闂5淖饔镁褪蔷S護(hù)函數(shù)調(diào)用的上下文)叼耙。 棧存儲(chǔ)區(qū)主要用來(lái)保存函數(shù)調(diào)用的時(shí)所需要的參數(shù)信息局部變量信息粒没、返回地址筛婉、寄存器信息等等。如果程序中沒(méi)有了棧存儲(chǔ)區(qū)癞松,那么程序幾乎無(wú)法運(yùn)行爽撒。

2. 棧的概念(與數(shù)據(jù)結(jié)果中的棧類比)

棧:一種后進(jìn)先出的行為。


棧的表現(xiàn)形式和在內(nèi)存中的表現(xiàn)形式

3. 棧在函數(shù)調(diào)用時(shí)的作用

問(wèn)題: 為什么說(shuō)函數(shù)調(diào)用時(shí)少不了棧响蓉?
因?yàn)楹瘮?shù)調(diào)用時(shí)硕勿,在內(nèi)存中需要維護(hù)一個(gè)活動(dòng)記錄,活動(dòng)記錄中包含了函數(shù)調(diào)用的參數(shù)枫甲,函數(shù)調(diào)用的返回地址源武,寄存器信息,局部變量想幻,其他數(shù)據(jù)信息(如臨時(shí)變量)等等粱栖,C語(yǔ)言中通過(guò)棧來(lái)維護(hù)活動(dòng)記錄中的信息。

內(nèi)存中的活動(dòng)記錄

4. 函數(shù)調(diào)用的過(guò)程

函數(shù)調(diào)用過(guò)程

總結(jié):每次函數(shù)調(diào)用都會(huì)對(duì)應(yīng)著在棧上建立一個(gè)新的活動(dòng)記錄脏毯,調(diào)用函數(shù)的活動(dòng)記錄位于棧的中部查排,被調(diào)函數(shù)的活動(dòng)記錄位于棧的頂部。

esp:棧頂指針
ebp:函數(shù)調(diào)用結(jié)束的返回地址

5. 函數(shù)調(diào)用的棧變化

  • 從main()開(kāi)始運(yùn)行抄沮,在棧中就會(huì)有main()中的活動(dòng)記錄跋核;


    main()運(yùn)行后棧中的狀態(tài)
  • main()調(diào)用f(),在棧中建立f()的活動(dòng)記錄叛买;


    f()運(yùn)行后棧中的狀態(tài)
  • 當(dāng)從f()調(diào)用中返回到main()
    當(dāng)f()運(yùn)行完后返回到main()后棧中的狀態(tài)

    椛按空間的數(shù)據(jù)不會(huì)因?yàn)楹瘮?shù)的返回而立即的改變,它只修改了esp指針和ebp指針中的地址值率挣,并沒(méi)有去改變椏桃粒空間中的數(shù)據(jù)。
    問(wèn)題:為什么不可以返回函數(shù)內(nèi)部變量的地址(局部變量地址)和局部數(shù)組椒功?
    因?yàn)殡m然棧內(nèi)存中的數(shù)據(jù)不會(huì)因?yàn)楹瘮?shù)的返回而改變捶箱,但是如果函數(shù)返回之后,又立即調(diào)用了另一個(gè)函數(shù)动漾,原來(lái)函數(shù)的內(nèi)存空間就會(huì)給另一個(gè)函數(shù)使用丁屎,這樣棧內(nèi)存中的原來(lái)函數(shù)的內(nèi)存空間就會(huì)發(fā)生變化。如果返回局部變量地址或局部數(shù)組是沒(méi)有意義的旱眯,甚至是錯(cuò)誤的晨川,因?yàn)槲覀兊闹羔標(biāo)赶虻木植孔兞炕蚓植繑?shù)組不存在了证九,這樣就會(huì)造成野指針的錯(cuò)誤而使得程序運(yùn)行崩潰。

6. 函數(shù)調(diào)用棧上的數(shù)據(jù)

  • 函數(shù)調(diào)用時(shí)共虑,對(duì)應(yīng)的椑⒘空間在函數(shù)返回前是專用的
  • 函數(shù)調(diào)用結(jié)束后妈拌,椨堤常空間將被釋放,數(shù)據(jù)不再有效尘分。
    程序說(shuō)明:指向棧數(shù)據(jù)的指針渴逻,函數(shù)返回后,棧中的數(shù)據(jù)沒(mé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("%d\n", b[i]);
    }
}

void main()
{
    f();

    return 0;
}

輸出結(jié)果:

0
0
0
0
0
0
0
0
0
0

程序說(shuō)明:指向棧數(shù)據(jù)的指針音诫,函數(shù)返回后惨奕,局部變量的數(shù)組不存在,會(huì)造成野指針的錯(cuò)誤

#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++)
    {
        printf("%d\n", pointer[i]);
    }
}

void main()
{
    f();

    return 0;
}

輸出結(jié)果:

0
-1217224704
0
0
-1074600104
-1218653521
-1217221952
134514048
-1074600172
-1218653568

總結(jié):活動(dòng)記錄銷毀后竭钝,原先活動(dòng)記錄里面的變量也就被銷毀梨撞,因此局部變量的地址也就沒(méi)有意義了,所以會(huì)出現(xiàn)野指針香罐。

7. 程序中的堆

問(wèn)題:如果說(shuō)在程序中需要一段額外的空間來(lái)完成任務(wù)卧波,比如數(shù)據(jù)結(jié)構(gòu)中經(jīng)常為了效率,用空間換時(shí)間庇茫,這個(gè)時(shí)候我們臨時(shí)的需要一段空間港粱,該怎么辦?——?jiǎng)討B(tài)內(nèi)存分配旦签。
動(dòng)態(tài)內(nèi)存分配是分配中的內(nèi)存查坪。

  • 堆是程序中一塊預(yù)留的內(nèi)存空間,為了是給程序自由使用宁炫。如我們臨時(shí)需要一段內(nèi)存空間偿曙,可以使用malloc來(lái)在堆內(nèi)存中取一塊來(lái)使用。
  • 堆中的內(nèi)存需要主動(dòng)的返還羔巢,因此堆中被程序申請(qǐng)使用的內(nèi)存在被主動(dòng)釋放前將一直有效望忆。
  • 如果只申請(qǐng)使用堆空間而不返還,會(huì)導(dǎo)致堆空間使用完畢竿秆, 程序會(huì)越運(yùn)行越慢启摄,最后導(dǎo)致程序無(wú)法運(yùn)行。
問(wèn)題: 棧的作用是什么幽钢?為什么有了棧還需要堆空間歉备?

棧的作用是為了函數(shù)調(diào)用。
在程序運(yùn)行過(guò)程中搅吁,需要臨時(shí)的一段內(nèi)存空間威创,因此需要堆空間來(lái)獲取一段臨時(shí)的內(nèi)存空間。

  • C語(yǔ)言程序中通過(guò)庫(kù)函數(shù)的調(diào)用獲得堆空間
    頭文件:malloc.h
    malloc:以字節(jié)的方式動(dòng)態(tài)申請(qǐng)堆空間
    free:將堆空間歸還給系統(tǒng)
系統(tǒng)對(duì)堆空間的管理方式——空閑鏈表法谎懦,位圖法肚豺,對(duì)象池法等等。

空閑鏈表法:

空閑鏈表法示意圖

C語(yǔ)言是以高效而聞名的界拦,因此malloc調(diào)用函數(shù)向堆內(nèi)存申請(qǐng)空間時(shí)也必須是高效的吸申。系統(tǒng)將堆中的內(nèi)存組織成一個(gè)鏈表,如上圖所示享甸,圖中每個(gè)節(jié)點(diǎn)的數(shù)字為對(duì)應(yīng)節(jié)點(diǎn)之下的每個(gè)單元的內(nèi)存大小是多少截碴。如12Bytes表示其下方內(nèi)存每個(gè)單元的大小為12Byte。當(dāng)程序調(diào)用malloc函數(shù)之后蛉威,系統(tǒng)就會(huì)遍歷這個(gè)空閑鏈表日丹,查找malloc所需要的內(nèi)存大小跟哪一個(gè)節(jié)點(diǎn)數(shù)的內(nèi)存大小最接近。如上圖中蚯嫌,申請(qǐng)int類型哲虾,即4個(gè)字節(jié)的大小,遍歷空閑鏈表后發(fā)現(xiàn)跟5Bytes節(jié)點(diǎn)最為接近择示,于是將會(huì)在其節(jié)點(diǎn)下方的內(nèi)存中尋找一個(gè)可用的單元束凑,找到后將單元地址返回給p指針。所以說(shuō)栅盲,malloc這個(gè)函數(shù)返回的可用空間可能會(huì)比申請(qǐng)的空間大汪诉,是因?yàn)橄到y(tǒng)通過(guò)空閑鏈表管理堆內(nèi)存時(shí),它會(huì)找malloc申請(qǐng)的最接近的那一個(gè)節(jié)點(diǎn)下對(duì)應(yīng)的堆內(nèi)存谈秫。

8.程序中的靜態(tài)存儲(chǔ)區(qū)

靜態(tài)存儲(chǔ)區(qū)隨著程序的運(yùn)行而分配扒寄,隨著程序的結(jié)束而結(jié)束。也就是說(shuō)靜態(tài)存儲(chǔ)區(qū)在程序運(yùn)行的那一刻就被系統(tǒng)給分配出來(lái)了拟烫,因此旗们,程序在編譯期的時(shí)候就已經(jīng)知道靜態(tài)存儲(chǔ)區(qū)的大小,運(yùn)行的時(shí)候僅僅給靜態(tài)存儲(chǔ)區(qū)分配空間构灸,且在運(yùn)行期靜態(tài)存儲(chǔ)區(qū)的大小是不能改變的上渴。靜態(tài)存儲(chǔ)區(qū)主要是用于保存全局變量和靜態(tài)局部變量。靜態(tài)存儲(chǔ)區(qū)的信息最終會(huì)保存在可執(zhí)行程序中喜颁,即靜態(tài)存儲(chǔ)區(qū)的大小信息稠氮、開(kāi)始信息、結(jié)束信息等保存在.exe或.out可執(zhí)行文件中半开。
靜態(tài)存儲(chǔ)區(qū)的驗(yà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;
}

輸出結(jié)果:

&g_v = 0x804a020
&g_vs = 0x804a024
&g_vl = 0x804a028

總結(jié):在內(nèi)存空間中隔披,g_v為全局變量,g_vl為靜態(tài)全局變量寂拆,g_vs為靜態(tài)局部變量奢米,這三個(gè)不同的變量存放在一段連續(xù)的存儲(chǔ)空間中(順序存放)抓韩,因?yàn)檫@個(gè)三個(gè)變量都是存放在靜態(tài)存儲(chǔ)區(qū)的。

9.小結(jié)

  • 棧鬓长、堆和靜態(tài)存儲(chǔ)區(qū)是程序中的三個(gè)基本數(shù)據(jù)區(qū)
  • 棧的主要作用是為了調(diào)用函數(shù)的時(shí)候維護(hù)對(duì)應(yīng)的活動(dòng)記錄谒拴;
  • 堆主要是用于內(nèi)存的動(dòng)態(tài)申請(qǐng)和歸還;
  • 靜態(tài)存儲(chǔ)區(qū)用于保存全局變量和靜態(tài)變量涉波。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末英上,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子啤覆,更是在濱河造成了極大的恐慌苍日,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窗声,死亡現(xiàn)場(chǎng)離奇詭異相恃,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)笨觅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門豆茫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人屋摇,你說(shuō)我怎么就攤上這事揩魂。” “怎么了炮温?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵火脉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我柒啤,道長(zhǎng)倦挂,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任担巩,我火速辦了婚禮方援,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涛癌。我一直安慰自己犯戏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布拳话。 她就那樣靜靜地躺著先匪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弃衍。 梳的紋絲不亂的頭發(fā)上呀非,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼岸裙。 笑死猖败,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的降允。 我是一名探鬼主播恩闻,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拟糕!你這毒婦竟也來(lái)了判呕?” 一聲冷哼從身側(cè)響起倦踢,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤送滞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后辱挥,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體犁嗅,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年晤碘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了褂微。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡园爷,死狀恐怖宠蚂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情童社,我是刑警寧澤求厕,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站扰楼,受9級(jí)特大地震影響呀癣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弦赖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一项栏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蹬竖,春花似錦沼沈、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至劈榨,卻和暖如春访递,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背同辣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工拷姿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惭载,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓响巢,卻偏偏與公主長(zhǎng)得像描滔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踪古,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容