试图理解C ++中的堆栈和堆

时间:2013-06-02 13:53:37

标签: c++ stack heap

我对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

}

问题:

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

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

2 个答案:

答案 0 :(得分:3)

您发布的代码中充满了错误,但不仅仅是您在评论中列出的错误。为了使用,普通C样式数组是不可分配的。因此,以下行不会将右侧数组的内容复制到左侧的数组中。

HeapArray = StackArray2;

C和C ++允许从数组隐式转换为指向数组第一个元素的指针;这通常称为衰减指向第一个元素的指针。因此,上面的语句会导致HeapArray指针指向StackArray2的开头。然后,当您在delete上致电HeapArray时,您正试图delete内存不是new。这是未定义的行为,会使您的程序崩溃(如果您很幸运)。

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

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


回答你的问题 -

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

  

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

并在运行时声明失败

  

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

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

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

答案 1 :(得分:2)

Q1。会发生什么叫做堆栈溢出但效果是你要超出堆栈的允许内存,因此你试图访问一些你无法访问的东西,所以从技术上讲它是一个分割故障

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

只需另外注意,调用堆栈还包含函数调用的激活记录,用于正确返回它们。