日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

关于栈的理解(读书笔记)

發(fā)布時(shí)間:2025/3/15 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于栈的理解(读书笔记) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

關(guān)于棧的理解(讀書筆記)

標(biāo)簽: 棧內(nèi)存布局可執(zhí)行程序 2957人閱讀 評(píng)論(1) 收藏 舉報(bào) 分類: 【C語(yǔ)言學(xué)習(xí)】(56)

目錄(?)[+]

? ? 關(guān)于對(duì)棧的理解,相信很多人和我一樣都是很模糊的。在昨天閱讀數(shù)據(jù)的時(shí)候,看到對(duì)這方面的介紹,便寫個(gè)這個(gè)博客來(lái)和大家分享下。希望對(duì)大家有所幫助。

? ? ?棧,是硬件,主要作用表現(xiàn)為一種數(shù)據(jù)結(jié)構(gòu),是只能在一端插入和刪除數(shù)據(jù)的特殊線性表。允許進(jìn)行插入和刪除操作的一端稱為棧頂,另一端為棧底。棧按照后進(jìn)先出的原則存儲(chǔ)數(shù)據(jù),最先進(jìn)入的數(shù)據(jù)被壓入棧底,最后進(jìn)入的數(shù)據(jù)在棧頂,需要讀數(shù)據(jù)時(shí)從棧頂開(kāi)始彈出數(shù)據(jù)。棧底固定,而棧頂浮動(dòng)。棧中元素個(gè)數(shù)為零時(shí)稱為空棧。插入一般稱為進(jìn)棧(p u s h ),刪除則稱為出棧(p o p )。 ?棧也被稱為先進(jìn)后出表,在函數(shù)調(diào)用的時(shí)候用于存儲(chǔ)斷點(diǎn),在遞歸時(shí)也要用到棧。
? ? ?在計(jì)算機(jī)系統(tǒng)中,棧則是一個(gè)具有以上屬性的動(dòng)態(tài)內(nèi)存區(qū)域。程序可以將數(shù)據(jù)壓入棧中,也可以將數(shù)據(jù)從棧頂彈出。在i 3 8 6 機(jī)器中,棧頂由稱為e s p 的寄存器進(jìn)行定位。壓棧的操作使棧頂?shù)牡刂窚p小,彈出的操作使棧頂?shù)牡刂吩龃蟆?棧在程序的運(yùn)行中有著舉足輕重的作用。最重要的是,棧保存了一個(gè)函數(shù)調(diào)用時(shí)所需要
的維護(hù)信息,這常常被稱為堆棧幀。棧一般包含以下兩方面的信息:?
1 )函數(shù)的返回地址和參數(shù)。
2 )臨時(shí)變量:包括函數(shù)的非靜態(tài)局部變量及編譯器自動(dòng)生成的其他臨時(shí)變量。?

? ? ? ?堆,是一種動(dòng)態(tài)存儲(chǔ)結(jié)構(gòu),實(shí)際上就是數(shù)據(jù)段中的自由存儲(chǔ)區(qū),它是C 語(yǔ)言中使用的一種名稱,常常用于存儲(chǔ)、分配動(dòng)態(tài)數(shù)據(jù)。堆中存入的數(shù)據(jù)地址向增加方向變動(dòng)。堆可以不斷進(jìn)行分配直到?jīng)]有堆空間為止,也可以隨時(shí)進(jìn)行釋放、再分配,不存在順序問(wèn)題。堆內(nèi)存的分配常通過(guò)m a l l o c ( ) 、c a l l o c ( ) 、r e a l l o c ( ) 三個(gè)函數(shù)來(lái)實(shí)現(xiàn)。而堆內(nèi)存的釋放則使用f r e e ( ) 函數(shù)。
? ? ?堆和棧在使用時(shí)“生長(zhǎng)”方向相反,棧向低地址方向“生長(zhǎng)”,而堆向高地址方向“生長(zhǎng)”。

? ? [csharp] view plaincopyprint?
  • #include?<stdio.h>??
  • void?print()??
  • {??
  • ????//?填充代碼???
  • }??
  • int?main()??
  • {???
  • ??int?a=1;??
  • ??int?b=2;??
  • ??char?c='c';??
  • ??int?arr[]={11,12,13,14,15,16,17};??
  • ???print();??
  • ???return?0;??
  • }??
  • ? ? ? 看看上面的代碼和相關(guān)要求,可能會(huì)讓很多讀者束手無(wú)策,如果能聯(lián)系前面的知識(shí)點(diǎn),就應(yīng)該想到用棧。那么我們?cè)撊绾蝸?lái)解決問(wèn)題呢?先別急,在講解之前,我們先來(lái)回顧幾個(gè)知識(shí)點(diǎn)。
    1 )p u s h 操作先移動(dòng)棧頂指針,之后將信息入棧。
    2 )e s p 為堆棧指針,棧頂由e s p 寄存器來(lái)定位。壓棧的操作使棧頂?shù)牡刂窚p小,彈出的操作使棧頂?shù)牡刂吩龃蟆?br /> 3 )e b p 是3 2 位的b p ,是基址指針。b p 為基指針寄存器,用它可直接存取堆棧中的數(shù)據(jù),它在調(diào)用函數(shù)時(shí)保存e s p ,以便函數(shù)結(jié)束時(shí)可以正確返回。
    4 )默認(rèn)的函數(shù)內(nèi)部變量的壓棧操作為:從上到下、從左向右,采用4 字節(jié)對(duì)齊。數(shù)組壓棧方法略有不同,即從最后一個(gè)元素開(kāi)始,直到起始元素為止,即采用從右向左的方法壓棧。
    ?函數(shù)的開(kāi)頭都有如下兩句匯編指令:
    push ? ? ? ?ebp
    mov ? ? ? ? ebp,esp
    為了使讀者易于理解,在此通過(guò)圖1 - 1 來(lái)分析說(shuō)明。根據(jù)圖上的標(biāo)注,函數(shù)開(kāi)頭部分的第一個(gè)p u s h 指令的操作步驟是,首先移動(dòng)棧頂指針e s p ,然后將e b p 內(nèi)容壓棧,注意此時(shí)壓棧的e b p 的值為上一個(gè)函數(shù)的e s p 的值, ?而e s p 恰好就是上一個(gè)函數(shù)的棧底,所以每個(gè)函數(shù)一開(kāi)始的p u s h 指令就是保存上一個(gè)函數(shù)的棧底。那么接下來(lái)的m o v 指令有什么作用呢?由于e s p 是當(dāng)前的棧頂指針,所以該指令的作用就是保存當(dāng)前棧頂指針的值。由此就可以分析出,e b p 存放的是此刻棧頂?shù)牡刂?#xff0c;就是說(shuō),e b p 是一個(gè)指針,指向棧頂,而棧頂存放的數(shù)據(jù)其實(shí)是上一個(gè)函數(shù)的e b p 的值,即上一個(gè)函數(shù)的棧底。



    ? ? 通過(guò)上面的分析可知,e b p 壓棧后,接著就是函數(shù)中臨時(shí)變量的壓棧操作,由此可知,我們只需要在p r i n t ( ) 函數(shù)中得到m a i n ( ) 函數(shù)的棧底,就可以取出數(shù)組中的每個(gè)元素了,看看下面的實(shí)現(xiàn)方法。

    [csharp] view plaincopyprint?
  • #include?<stdio.h>??
  • void?print()??
  • {??
  • ????unsigned?int?_ebp;??
  • ????__asm{??
  • ????????mov?_ebp,ebp??
  • ????}??
  • ????int?*p=(int?*)(*(int?*)_ebp-4-4-4-7*4);??
  • ????for(int?i=0;i<7;i++)??
  • ????????printf("%d\t",p[i]);??
  • }??
  • int?main()??
  • {???
  • ????int?a=1;??
  • ????int?b=2;??
  • ????char?c='a';??
  • ????int?arr[]={11,12,13,14,15,16,17};??
  • ????print();??
  • ????return?0??
  • }??
  • 注:關(guān)于GCC內(nèi)聯(lián)匯編代碼編譯,有知道的大哥給指點(diǎn)下:這個(gè)網(wǎng)上也沒(méi)有搜索到具體如何編譯。知道的麻煩給回復(fù)下

    ? ? ?在沒(méi)有傳遞任何參數(shù)的情況下,成功地在p r i n t ( ) 函數(shù)中打印出了m a i n ( ) 函數(shù)中a r r 數(shù)組內(nèi)的每個(gè)元素。現(xiàn)在來(lái)看看上面代碼的實(shí)現(xiàn)方法,在p r i n t ( ) 函數(shù)中定義了一個(gè)_ e b p 無(wú)符號(hào)整型變量,通過(guò)V C + + ? 6 . 0 內(nèi)嵌匯編把e b p 的值保持到_ e b p 中,按照上面的分析,可以將在函數(shù)p r i n t ( ) 中通過(guò)這條內(nèi)嵌匯編語(yǔ)句得到的e b p 看成一個(gè)指針,指針?biāo)赶虻膯卧娣诺木褪莗 r i n t ( ) 函數(shù)的上一個(gè)函數(shù)的棧底,在此是m a i n ( ) 函數(shù)的棧底。知道了_ e b p 的作用后,我們來(lái)分析下代碼,通過(guò)( i n t * ) _ e b p 將_ e b p 轉(zhuǎn)換為一個(gè)整型指針,然后通過(guò)* ( i n t * ) _ e b p 即可得到m a i n ( ) 函數(shù)的棧底地址。由于棧的壓棧操作是從上到下、從右到左的,所以m a i n ( ) 函數(shù)中的變量a 先壓棧,然后是b 、c ,最后是a r r 數(shù)組,數(shù)組的壓棧順序是從右到左。通過(guò)“i n t ?* p = ( i n t ? * ) ( * ( i n t ? * ) _ e b p - 4 - 4 - 4 - 7 * 4 ) ; ”即可得到數(shù)組元素的首地址。接下來(lái),根據(jù)首地址就可以取出數(shù)組中的每個(gè)元素了。有的讀者可能會(huì)有一個(gè)疑惑,m a i n ( ) 函數(shù)中有一個(gè)字符型變量,是不是在求數(shù)組元素的首地址時(shí)應(yīng)該把其中的減4 改為減1 呢?因?yàn)樗徽加昧艘粋€(gè)字節(jié)!即將“i n t ? * p = ( i n t ? * ) ( * ( i n t ? * ) _ e b p - 4 - 4 - 4 - 7 * 4 ) ; ”修改為“i n t ? * p = ( i n t ? * ) ( * ( i n t ? * ) _ e b p - 4 - 4 - 1 -7 * 4 ) ”。我們暫且不說(shuō)其對(duì)與錯(cuò),先來(lái)看看修改后的運(yùn)行結(jié)果:3072 ? ?3328 ? ?3584 ? ?3840 ? ?4096 ? ?4352 ? ?-859021056 ? ? ?
    ? ? ?我們發(fā)現(xiàn)這樣的運(yùn)行結(jié)果是錯(cuò)誤的,為什么呢?細(xì)心的讀者可能發(fā)現(xiàn)了本章一開(kāi)始回顧的知識(shí)點(diǎn)中有一點(diǎn)是很重要的,那就是壓棧操作為4 字節(jié)對(duì)齊。所以這里必須減4 ,而不是減1?

    -------------------------------------------------------------以上是書本原話介紹---------------------------------------

    ? ? 閱讀完上面的代碼應(yīng)該會(huì)對(duì)棧有一定的了解。下面簡(jiǎn)單介紹下可執(zhí)行程序在內(nèi)存中的布局!

    ? ? ?

    ? ? ?數(shù)據(jù)段包含經(jīng)過(guò)初始化的全局和靜態(tài)變量及他們的數(shù)值。BSS段的大小從可執(zhí)行程序中得到,然后又連接器得到這個(gè)大小的內(nèi)存塊,緊跟在數(shù)據(jù)段的后面。當(dāng)這個(gè)內(nèi)存區(qū)進(jìn)行程序的地址空間后全部清零。包括數(shù)據(jù)段和BSS段的整個(gè)區(qū)域統(tǒng)稱為數(shù)據(jù)區(qū)。值得說(shuō)明的是局部變量并未編譯到可執(zhí)行程序中,是在程序運(yùn)行時(shí)執(zhí)行的。


    總結(jié)

    以上是生活随笔為你收集整理的关于栈的理解(读书笔记)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。