1

我对 C++ 很陌生,并试图尽可能多地理解堆栈和堆的概念(或者至少我需要知道的)。有些人倾向于说启动器不应该那么麻烦,但在我看来,内存泄漏或堆栈溢出很容易发生。我一直在阅读一些东西,但我仍然有点困惑,不确定我是否做对了。

这是我到目前为止所得到的......

1.堆:

堆是一个共享且动态分配的区域。我们流程的任何部分都可以通过正确的指针和内容(类型和长度)知识来访问它。如果我们尝试使用错误的指针(普通地址或释放指针)将导致分段错误。访问比分配的内容更大的内容也会导致分段错误(例如,尝试读取比分配的更大的数组)。必须“手动”释放未使用的区域以避免内存泄漏

2.堆栈:

堆栈是分配参数和局部变量的内存部分。堆栈的大小是有限的。堆栈作为 LIFO(后进先出)工作。

假设堆栈是一个预定义大小(堆栈大小)的箱。当我们定义局部变量时,它们被放入堆栈(放入 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

}

问题 :

Q1。定义一个对于堆栈来说太大的局部变量或者像我上面的示例那样具有无限递归函数会给我一个分段错误。为什么这不是说“堆栈溢出”?是因为堆栈“溢出到堆中”并造成分段错误吗?

Q2。假设我为堆栈提供的 bin 和 platters 示例:使用时extern内容是复制到最后一个盘子顶部的新范围还是创建了某种指针?

4

2 回答 2

3

您发布的代码充满了错误,但不仅仅是您在评论中列出的错误。与之相伴的是,纯 C 风格的数组是不可分配的。因此,以下行不会将右侧数组的内容复制到左侧数组中。

HeapArray = StackArray2;

C 和 C++ 允许从数组到指向数组第一个元素的指针的隐式转换;这通常称为衰减到指向第一个元素的指针。因此,上面的语句使HeapArray指针指向StackArray2. 然后,当您调用deleteHeapArray,您正在尝试delete记忆未new编辑的内容。这是未定义的行为,并且会使您的程序崩溃(如果幸运的话)。

除此之外,您还泄漏了通过 分配的内存new,因为您现在丢失了指向该内存的唯一指针。

同样,下一个分配 toHeapArray是分配 to 的StackArray1地址HeapArray。因为您只分配指针,所以这一行没有错误;该程序将继续正常执行(但由于之前的删除,您可能已经崩溃了)。


回答你的问题——

1 - 无法保证堆栈溢出或错误删除总是会以可预测的方式失败。它还取决于您使用的编译器。如果我注释掉 中的所有代码MyFunction(),除了对自身的递归调用,g++ 4.8 不会发出任何警告,并且会因分段错误而失败。但是,VS2012 发出警告

警告 C4717:“MyFunction”:在所有控制路径上递归,函数将导致运行时堆栈溢出

并在运行时失败说明

Test.exe 中 0x00062949 处未处理的异常:0xC00000FD:堆栈溢出(参数:0x00000001、0x00802FA4)

但是,那是在禁用优化的情况下。启用优化后,程序会无限运行(使用两个编译器)。这可能是因为代码非常简单,以至于两个编译器都用无限循环替换了对 self 的递归调用。现在不会有堆栈溢出,但你的程序也永远不会终止。

2 - 全局变量(您extern从另一个翻译单元访问的变量)不存储在堆栈中。它们存储在与堆栈不同的实现定义的内存区域中。

于 2013-06-02T14:31:02.623 回答
2

Q1。发生的事情称为堆栈溢出,但结果是您超出了堆栈允许的内存,因此您试图访问您无法访问的内容,因此从技术上讲这是一个分段错误

Q2。您的示例不适合,因为堆栈没有隐藏任何先前的内容,任何指向堆栈深处的任何内容的指针仍然有效,它不会被连续调用隐藏。它之所以称为堆栈,是因为它的行为类似于堆栈(分配的数据在其顶部增长),但在其下方的任何内容都只是内存,如果您保留指向它的指针,则可以合法访问。

需要注意的是,调用堆栈还包含函数调用的激活记录,这些记录用于能够正确地从它们返回。

于 2013-06-02T13:59:20.853 回答