• <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>

            永遠也不完美的程序

            不斷學習,不斷實踐,不斷的重構……

            常用鏈接

            統計

            積分與排名

            好友鏈接

            最新評論

            堆和棧(轉)

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

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

            和棧不同,堆的數據結構并不是由系統(無論是機器系統還是操作系統)支持的,而是由函數庫提供的?;镜膍alloc/realloc/free函數維護了一套內部的堆數據結構。當程序使用這些函數去獲得新的內存空間時,這套函數首先試圖從內部堆中尋找可用的內存空間,如果沒有可以使用的內存空間,則試圖利用系統調用來動態增加程序數據段的內存大小,新分配得到的空間首先被組織進內部堆中去,然后再以適當的形式返回給調用者。當程序釋放分配的內存空間時,這片內存空間被返回內部堆結構中,可能會被適當的處理(比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內存分配申請。這套復雜的分配機制實際上相當于一個內存分配的緩沖池(Cache),使用這套機制有如下若干原因:

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

            2. 系統調用申請內存可能是代價昂貴的。系統調用可能涉及用戶態和核心態的轉換。

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

            堆和棧的對比

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





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

                  當程序被操作系統調入內存運行, 其相對應的進程在內存中的影像如下圖所示.  

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

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

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

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

            評論

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

            解釋的很清楚哦,不錯!

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

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

            niubi, 學習了. thanks  回復  更多評論   

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

            引用:堆的數據結構并不是由系統(無論是機器系統還是操作系統)支持的,而是由函數庫提供的.
            ----------------------------------
            應該是有系統提供的,操作系統幾大組件,內存管理,文件管理。
            不是系統提供內存管理系統調用,庫函數只是空中樓閣,必定庫函數也是封裝了系統調用  回復  更多評論   

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

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

            欧美激情一区二区久久久| 久久精品国产99久久无毒不卡| 99久久精品毛片免费播放| 99久久精品国产麻豆| 久久久久亚洲精品天堂久久久久久| 久久夜色撩人精品国产| 99精品国产99久久久久久97| 国产成人综合久久综合| 性做久久久久久久久久久| 欧洲人妻丰满av无码久久不卡| www亚洲欲色成人久久精品| 久久经典免费视频| 国产精品久久免费| 狠狠综合久久综合88亚洲| 久久久中文字幕| 日韩精品久久无码中文字幕| 久久久久九九精品影院| 国产精品无码久久综合| 久久99国产精品久久99小说| 久久中文娱乐网| 久久精品国产亚洲AV无码麻豆 | 久久亚洲国产精品一区二区| 欧美精品丝袜久久久中文字幕 | 久久99精品国产99久久6男男| 久久男人中文字幕资源站| 日本道色综合久久影院| 久久天天躁狠狠躁夜夜网站| 亚洲国产成人久久综合区| 国产免费久久精品丫丫| 国产一区二区三区久久| 久久久精品人妻一区二区三区四| 久久天天躁夜夜躁狠狠| 久久人人爽人人爽人人爽 | 亚洲AV日韩精品久久久久| 久久综合九色综合久99| 久久久艹| 欧美性猛交xxxx免费看久久久| 国产精品熟女福利久久AV| 精品久久久久久99人妻| 久久久久亚洲AV无码去区首| 热RE99久久精品国产66热|