簡體   English   中英

試圖理解 C++ 中的堆棧和堆

[英]Trying to understand stack and heap in C++

我對 C++ 很陌生,並試圖盡可能多地理解堆棧和堆的概念(或至少盡可能多地了解)。 有些人傾向於說初學者不應該那么麻煩,但在我看來,內存泄漏或堆棧溢出很容易發生。 我一直在閱讀一些東西,但我仍然有點困惑,不確定我是否做對了。

這是我到目前為止所得到的......

1. 堆:

堆是一個共享且動態分配的區域。 我們流程的任何部分都可以使用正確的指針和內容知識(類型和長度)訪問它。 如果我們嘗試使用錯誤的指針(平凡地址或釋放指針)將導致分段錯誤。 訪問比分配更大的內容也會導致分段錯誤(例如,嘗試讀取比分配更大的數組)。 未使用的區域必須“手動”釋放以避免內存泄漏

2. 堆棧:

堆棧是分配參數和局部變量的內存部分。 堆棧的大小有限。 堆棧作為 LIFO(后進先出)工作。

假設堆棧是一個預定義大小(堆棧大小)的 bin。 當我們定義局部變量時,它們被放在堆棧上(進入 bin),並且一旦作用域發生變化(例如調用一個函數),我們的 bin 中就會使用一個盤片,以防止訪問先前作用域中定義的變量和一個新的局部變量范圍已創建。 一旦函數結束,所有局部變量都會被銷毀,並且我們 bin 中的盤片被移除(返回到之前的作用域)。

例子 :

void MyFunction()
{
    int *HeapArray = new int[10];
    // HeapArray is assigned 40 bytes from the heap
    // *HeapArray is assigned 4 bytes from the stack in a 32 bit environment

    int StackArray1[10];
    // StackArray is assigned 40 bytes from the stack

    int StackArray2[20];
    // StackArray is assigned 80 bytes from the stack

    HeapArray = StackArray2;
    // segmentation fault because StackArray it too large

    delete HeapArray;
    // this will deallocate the area assigned in the heap
    // omitting delete would result in memory leaks
    // the pointer itself *HeapArray continues to exist in the stack

    HeapArray = StackArray1;
    // segmentation fault because HeapArray is pointing to deallocated memory

    MyFunction();
    // this will result in a stack overflow

}

問題 :

一季度。 定義一個對於堆棧來說太大的局部變量或者像上面的例子那樣有一個無限的遞歸函數會給我一個分段錯誤。 為什么這不是說“堆棧溢出”? 是不是因為堆棧“溢出到堆中”並造成分段錯誤?

Q2。 假設我為堆棧提供的 bin 和盤片示例:使用extern ,內容是復制到最后一個盤片頂部的新范圍還是創建了某種指針?

您發布的代碼充滿了錯誤,但不僅僅是您在評論中列出的那些。 順便說一句,普通的 C 風格數組是不可分配的。 因此,以下行不會將右側數組的內容復制到左側的數組中。

HeapArray = StackArray2;

C 和 C++ 允許從數組隱式轉換為指向數組第一個元素的指針; 這通常稱為衰減到指向第一個元素的指針。 因此,上面的語句導致HeapArray指針指向StackArray2的開頭。 然后,當您在HeapArray上調用delete時,您正在嘗試delete不是new ed 的內存。 這是未定義的行為,會導致程序崩潰(如果幸運的話)。

除此之外,您還泄漏了通過new分配的內存,因為您現在丟失了指向該內存的唯一指針。

同樣,下一個任務來HeapArray被分配的地址StackArray1HeapArray 因為你只是在分配指針,所以這一行沒有錯誤 該程序將繼續正常執行(但由於之前的刪除,您可能已經崩潰了)。


回答你的問題——

1 - 無法保證堆棧溢出或錯誤刪除總是以可預測的方式失敗。 它還取決於您使用的編譯器。 如果我注釋掉MyFunction()所有代碼,除了對自身的遞歸調用,g++ 4.8 不會發出警告,並且會因分段錯誤而失敗。 但是,VS2012 發出警告

警告 C4717:“MyFunction”:在所有控制路徑上遞歸,函數將導致運行時堆棧溢出

並在運行時失敗說明

Test.exe 中 0x00062949 處未處理的異常:0xC00000FD:堆棧溢出(參數:0x00000001、0x00802FA4)

但是,那是在禁用優化的情況下。 啟用優化后,程序將無休止地運行(使用兩個編譯器)。 這可能是因為代碼足夠簡單,以至於兩個編譯器都將遞歸調用 self 替換為無限循環。 現在永遠不會有堆棧溢出,但您的程序也永遠不會終止。

2 -全局變量(那些你extern從另一個翻譯單元訪問)沒有存儲在堆棧上。 它們存儲在與堆棧不同的實現定義的內存區域中。

一季度。 發生的情況稱為堆棧溢出,但結果是您超出了堆棧的允許內存范圍,因此您試圖訪問您無法訪問的內容,因此從技術上講,這是一個分段錯誤

Q2。 您的示例不適合,因為堆棧不會隱藏任何先前的內容,任何指向堆棧中更深處任何內容的指針仍然有效,它不會被連續調用隱藏。 之所以稱為堆棧,是因為它的行為類似於堆棧(分配的數據在其頂部增長),但下方的任何內容都只是內存,如果您保留指向它的指針,則可以合法訪問。

只是附加說明,調用堆棧還包含函數調用的激活記錄,用於能夠從它們正確返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM