• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            永遠也不完美的程序

            不斷學習,不斷實踐,不斷的重構(gòu)……

            常用鏈接

            統(tǒng)計

            積分與排名

            好友鏈接

            最新評論

            堆和棧(轉(zhuǎn))

            堆(heap)和棧(stack)是C/C++編程不可避免會碰到的兩個基本概念。首先,這兩個概念都可以在講數(shù)據(jù)結(jié)構(gòu)的書中找到,他們都是基本的數(shù)據(jù)結(jié)構(gòu),雖然棧更為簡單一些。在具體的C/C++編程框架中,這兩個概念并不是并行的。對底層機器代碼的研究可以揭示,棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),而堆則是C/C++函數(shù)庫提供的。

            具體地說,現(xiàn)代計算機(串行執(zhí)行機制),都直接在代碼底層支持棧的數(shù)據(jù)結(jié)構(gòu)。這體現(xiàn)在,有專門的寄存器指向棧所在的地址,有專門的機器指令完成數(shù)據(jù)入棧出棧的操作。這種機制的特點是效率高,支持的數(shù)據(jù)有限,一般是整數(shù),指針,浮點數(shù)等系統(tǒng)直接支持的數(shù)據(jù)類型,并不直接支持其他的數(shù)據(jù)結(jié)構(gòu)。因為棧的這種特點,對棧的使用在程序中是非常頻繁的。對子程序的調(diào)用就是直接利用棧完成的。機器的call指令里隱含了把返回地址推入棧,然后跳轉(zhuǎn)至子程序地址的操作,而子程序中的ret指令則隱含從堆棧中彈出返回地址并跳轉(zhuǎn)之的操作。C/C++中的自動變量是直接利用棧的例子,這也就是為什么當函數(shù)返回時,該函數(shù)的自動變量自動失效的原因。

            和棧不同,堆的數(shù)據(jù)結(jié)構(gòu)并不是由系統(tǒng)(無論是機器系統(tǒng)還是操作系統(tǒng))支持的,而是由函數(shù)庫提供的。基本的malloc/realloc/free函數(shù)維護了一套內(nèi)部的堆數(shù)據(jù)結(jié)構(gòu)。當程序使用這些函數(shù)去獲得新的內(nèi)存空間時,這套函數(shù)首先試圖從內(nèi)部堆中尋找可用的內(nèi)存空間,如果沒有可以使用的內(nèi)存空間,則試圖利用系統(tǒng)調(diào)用來動態(tài)增加程序數(shù)據(jù)段的內(nèi)存大小,新分配得到的空間首先被組織進內(nèi)部堆中去,然后再以適當?shù)男问椒祷亟o調(diào)用者。當程序釋放分配的內(nèi)存空間時,這片內(nèi)存空間被返回內(nèi)部堆結(jié)構(gòu)中,可能會被適當?shù)奶幚?比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內(nèi)存分配申請。這套復(fù)雜的分配機制實際上相當于一個內(nèi)存分配的緩沖池(Cache),使用這套機制有如下若干原因:

            1. 系統(tǒng)調(diào)用可能不支持任意大小的內(nèi)存分配。有些系統(tǒng)的系統(tǒng)調(diào)用只支持固定大小及其倍數(shù)的內(nèi)存請求(按頁分配);這樣的話對于大量的小內(nèi)存分類來說會造成浪費。

            2. 系統(tǒng)調(diào)用申請內(nèi)存可能是代價昂貴的。系統(tǒng)調(diào)用可能涉及用戶態(tài)和核心態(tài)的轉(zhuǎn)換。

            3. 沒有管理的內(nèi)存分配在大量復(fù)雜內(nèi)存的分配釋放操作下很容易造成內(nèi)存碎片。

            堆和棧的對比

            從以上知識可知,棧是系統(tǒng)提供的功能,特點是快速高效,缺點是有限制,數(shù)據(jù)不靈活;而棧是函數(shù)庫提供的功能,特點是靈活方便,數(shù)據(jù)適應(yīng)面廣泛,但是效率有一定降低。棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對于進程/線程是唯一的;堆是函數(shù)庫內(nèi)部數(shù)據(jù)結(jié)構(gòu),不一定唯一。不同堆分配的內(nèi)存無法互相操作。棧空間分靜態(tài)分配和動態(tài)分配兩種。靜態(tài)分配是編譯器完成的,比如自動變量(auto)的分配。動態(tài)分配由alloca函數(shù)完成。棧的動態(tài)分配無需釋放(是自動的),也就沒有釋放函數(shù)。為可移植的程序起見,棧的動態(tài)分配操作是不被鼓勵的!堆空間的分配總是動態(tài)的,雖然程序結(jié)束時所有的數(shù)據(jù)空間都會被釋放回系統(tǒng),但是精確的申請內(nèi)存/釋放內(nèi)存匹配是良好程序的基本要素。





            進程在內(nèi)存中的影像.  
                  我們假設(shè)現(xiàn)在有一個程序, 它的函數(shù)調(diào)用順序如下.  
                  main(...) -> func_1(...) -> func_2(...) -> func_3(...)  
                  即: 主函數(shù)main調(diào)用函數(shù)func_1; 函數(shù)func_1調(diào)用函數(shù)func_2; 函數(shù)func_2調(diào)用函數(shù)func_3  

                  當程序被操作系統(tǒng)調(diào)入內(nèi)存運行, 其相對應(yīng)的進程在內(nèi)存中的影像如下圖所示.  

                    (內(nèi)存高址)  
                    +--------------------------------------+  
                    |             ......                   |  ... 省略了一些我們不需要關(guān)心的區(qū)  
                    +--------------------------------------+  
                    |  env strings (環(huán)境變量字串)          | \  
                    +--------------------------------------+  \  
                    |  argv strings (命令行字串)           |   \  
                    +--------------------------------------+    \  
                    |  env pointers (環(huán)境變量指針)         |    SHELL的環(huán)境變量和命令行參數(shù)保存區(qū)  
                    +--------------------------------------+    /  
                    |  argv pointers (命令行參數(shù)指針)      |   /  
                    +--------------------------------------+  /  
                    |  argc (命令行參數(shù)個數(shù))               | /  
                    +--------------------------------------+  
                    |            main 函數(shù)的棧幀           | \  
                    +--------------------------------------+  \  
                    |            func_1 函數(shù)的棧幀         |   \  
                    +--------------------------------------+    \  
                    |            func_2 函數(shù)的棧幀         |     \  
                    +--------------------------------------+      \  
                    |            func_3 函數(shù)的棧幀         |      Stack (棧)  
                    +......................................+      /  
                    |                                      |     /  
                                  ......                        /  
                    |                                      |   /  
                    +......................................+  /  
                    |            Heap (堆)                 | /  
                    +--------------------------------------+  
                    |        Uninitialised (BSS) data      |  非初始化數(shù)據(jù)(BSS)區(qū)  
                    +--------------------------------------+  
                    |        Initialised data              |  初始化數(shù)據(jù)區(qū)  
                    +--------------------------------------+  
                    |        Text                          |  文本區(qū)  
                    +--------------------------------------+  
                    (內(nèi)存低址)  

                    這里需要說明的是:  
                    i)   隨著函數(shù)調(diào)用層數(shù)的增加, 函數(shù)棧幀是一塊塊地向內(nèi)存低地址方向延伸的.  
                         隨著進程中函數(shù)調(diào)用層數(shù)的減少, 即各函數(shù)調(diào)用的返回, 棧幀會一塊塊地  
                         被遺棄而向內(nèi)存的高址方向回縮.  
                         各函數(shù)的棧幀大小隨著函數(shù)的性質(zhì)的不同而不等, 由函數(shù)的局部變量的數(shù)目決定.  
                    ii)  進程對內(nèi)存的動態(tài)申請是發(fā)生在Heap(堆)里的. 也就是說, 隨著系統(tǒng)動態(tài)分  
                         配給進程的內(nèi)存數(shù)量的增加, Heap(堆)有可能向高址或低址延伸, 依賴于不  
                         同CPU的實現(xiàn). 但一般來說是向內(nèi)存的高地址方向增長的.  
                    iii) 在BSS數(shù)據(jù)或者Stack(棧)的增長耗盡了系統(tǒng)分配給進程的自由內(nèi)存的情況下,  
                         進程將會被阻塞, 重新被操作系統(tǒng)用更大的內(nèi)存模塊來調(diào)度運行.  
                         (雖然和exploit沒有關(guān)系, 但是知道一下還是有好處的)  
                    iv)  函數(shù)的棧幀里包含了函數(shù)的參數(shù)(至于被調(diào)用函數(shù)的參數(shù)是放在調(diào)用函數(shù)的棧  
                         幀還是被調(diào)用函數(shù)棧幀, 則依賴于不同系統(tǒng)的實現(xiàn)),  
                         它的局部變量以及恢復(fù)調(diào)用該函數(shù)的函數(shù)的棧幀(也就是前一個棧幀)所需要的  
                         數(shù)據(jù), 其中包含了調(diào)用函數(shù)的下一條執(zhí)行指令的地址.  
                    v)   非初始化數(shù)據(jù)(BSS)區(qū)用于存放程序的靜態(tài)變量, 這部分內(nèi)存都是被初始化為零的.  
                         初始化數(shù)據(jù)區(qū)用于存放可執(zhí)行文件里的初始化數(shù)據(jù).  
                         這兩個區(qū)統(tǒng)稱為數(shù)據(jù)區(qū).  
                    vi)  Text(文本區(qū))是個只讀區(qū), 任何嘗試對該區(qū)的寫操作會導致段違法出錯. 文本區(qū)  
                         是被多個運行該可執(zhí)行文件的進程所共享的. 文本區(qū)存放了程序的代碼.  

                2) 函數(shù)的棧幀.  
                   函數(shù)調(diào)用時所建立的棧幀包含了下面的信息:  
                   i)   函數(shù)的返回地址. 返回地址是存放在調(diào)用函數(shù)的棧幀還是被調(diào)用函數(shù)的棧幀里,  
                        取決于不同系統(tǒng)的實現(xiàn).  
                   ii)  調(diào)用函數(shù)的棧幀信息, 即棧頂和棧底.  
                   iii) 為函數(shù)的局部變量分配的空間  
                   iv)  為被調(diào)用函數(shù)的參數(shù)分配的空間--取決于不同系統(tǒng)的實現(xiàn).

            posted on 2009-05-16 23:52 狂爛球 閱讀(2542) 評論(4)  編輯 收藏 引用 所屬分類: C++

            評論

            # re: 堆和棧(轉(zhuǎn)) 2009-05-17 12:22 skyscribe

            解釋的很清楚哦,不錯!

            補充一點:
            函數(shù)的參數(shù)可能是在寄存器上而不是在棧上。
            gcc那個著名的優(yōu)化選項-fomit-frame-pointer還可以把fp指針占用的寄存器空間給省略掉從而帶來性能的提升。  回復(fù)  更多評論   

            # re: 堆和棧(轉(zhuǎn)) 2009-05-17 12:28 maomi

            niubi, 學習了. thanks  回復(fù)  更多評論   

            # re: 堆和棧(轉(zhuǎn))[未登錄] 2009-05-18 00:17 zj

            引用:堆的數(shù)據(jù)結(jié)構(gòu)并不是由系統(tǒng)(無論是機器系統(tǒng)還是操作系統(tǒng))支持的,而是由函數(shù)庫提供的.
            ----------------------------------
            應(yīng)該是有系統(tǒng)提供的,操作系統(tǒng)幾大組件,內(nèi)存管理,文件管理。
            不是系統(tǒng)提供內(nèi)存管理系統(tǒng)調(diào)用,庫函數(shù)只是空中樓閣,必定庫函數(shù)也是封裝了系統(tǒng)調(diào)用  回復(fù)  更多評論   

            # re: 堆和棧(轉(zhuǎn)) 2009-05-18 11:20 yb

            @zj
            沒錯,malloc,free之類的函數(shù)需要系統(tǒng)支持,這就是為什么不同的平臺移植時往往要重寫malloc, free。  回復(fù)  更多評論   

            国产精品亚洲综合专区片高清久久久 | 久久受www免费人成_看片中文 | 91精品国产91久久久久久青草 | 2022年国产精品久久久久| 97久久国产露脸精品国产| 99久久精品国产一区二区| 久久久久久精品免费看SSS| 亚洲欧洲日产国码无码久久99| 五月丁香综合激情六月久久| 久久综合国产乱子伦精品免费| 国产精品久久久久影院嫩草| 国产L精品国产亚洲区久久| 久久综合成人网| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 99精品国产在热久久| 久久99精品久久久久久噜噜| 伊人色综合九久久天天蜜桃| 久久夜色精品国产网站| 国产香蕉97碰碰久久人人| 亚洲精品无码久久毛片| 99久久人妻无码精品系列蜜桃| 国产成人精品久久| 一级做a爰片久久毛片免费陪| 亚洲av成人无码久久精品| 88久久精品无码一区二区毛片 | 久久免费看黄a级毛片| 久久国产精品久久国产精品| 日韩欧美亚洲国产精品字幕久久久 | 国产午夜电影久久| 色播久久人人爽人人爽人人片AV| 久久―日本道色综合久久| 一级做a爰片久久毛片毛片| 91精品久久久久久无码| 欧美va久久久噜噜噜久久| 欧美激情精品久久久久久久九九九 | 亚洲AV无码一区东京热久久| 久久久久久久久久久免费精品| 久久久精品2019免费观看| 思思久久精品在热线热| 久久影院亚洲一区| 久久久久久久99精品免费观看|