堆栈地址是否由堆地址共享?

时间:2010-06-18 15:14:21

标签: c++ c visual-studio visual-c++

我读过在大多数操作系统上,内存中的地址从最高到最低。所以我想知道堆,堆栈和全局内存是否都属于相同的排序..?

如果我创造了......

pointerType* pointer = new pointerType  //creates memory address 0xffffff

然后在堆栈上创建了一个局部变量

localObject object

localObjects地址是0xfffffe

或者堆和堆栈的排序完全不同。

9 个答案:

答案 0 :(得分:3)

堆栈和堆通常位于内存中两个非常不同的位置。

答案 1 :(得分:3)

通常,您会发现堆栈从某个起始地址向下增长,堆从一些完全不同的起始地址向上增长。

堆栈和堆(它在C ++标准中称为动态存储)如何正常工作虽然是实现定义的,但不受C ++标准的约束。

答案 2 :(得分:2)

这取决于。在典型的CPU上,您拥有一个地址空间,因此任何两个变量必须具有不同的地址。但是,大多数都支持虚拟寻址,在这种情况下,两个不同的虚拟地址可能会引用相同的物理地址。

在DSP(例如)上,您通常有两个或三个完全独立的地址空间 - 例如,您的代码和数据将具有完全独立的物理寻址(即一个连接到一个存储器总线用于数据的存储器芯片组,以及连接到用于代码的单独总线的单独芯片组。在这种情况下,一个物理地址可以指内存的两个独立部分;在不知道它想要引用的东西 kind 的情况下,地址可能不允许你区分这两者。

答案 3 :(得分:2)

在现代操作系统下,它比这更复杂,但是为了帮助您入门:

在大多数个人计算机类系统(*)中,堆栈和堆都是同一地址空间的一部分,但按照惯例,堆栈通常从高地址开始,随着东西被推到它们上而向下增长,而堆是最好被认为是不属于堆栈的任何RAM,全局变量空间(.data和.bss)或程序代码(.text)的一部分,两者也在同一地址空间中。

  • 这有点谎言,因为x86处理器支持分段,这会使事情变得混乱,但这是关于计算机的使用方式,而不是严格意义上的支持。

这通常被称为von Newman架构 - 所有数据和代码都存在于一个通用地址空间中。

另一种架构类型叫做哈佛。在哈佛的archetectures代码存在于ROM中,大多数数据存在于RAM中,RAM和ROM不共享地址空间。这意味着函数和变量可以具有相同的数字地址,但仍然不在同一位置。 Atmel的AVR架构就是一个很好的例子,除了一些较小的版本只有没有RAM的寄存器(这只会混淆了这个问题)和一些将地址空间从16位扩展到24位的较大版本。可能(不确定)会模糊地址空间的边界。

8051系列处理器仍然不同,与您的问题更相关。通常,它们具有少量的快速RAM,它是堆栈区域,并且与通用RAM位于不同的地址空间中,通用RAM是全局变量并且可能存在堆区域的地方。它们通常还将代码放在另一个单独的地址空间中。

堆是什么

堆实际上只是一种处理尚未使用的内存/地址空间的方法。用于从此备用内存分配和释放内存的算法使其成为

堆栈究竟是什么

堆栈通常有点不同,因为通常以调用,返回,推送和弹出的指令形式为它们提供硬件支持,但这些甚至都不是必需的。当您开始处理中断时,系统堆栈的硬件支持变得必要,因为处理器的“发生中断”操作需要在执行中断服务例程之前使用堆栈来保存处理器的状态。

为什么这通常是桌面计算机上的谎言

现代硬件上的现代桌面/工作站操作系统通常能够利用内存管理硬件来做各种事情,以扭曲应用程序对地址空间的看法,从实际到应用程序更简单的东西处理并允许多个应用程序共享RAM。

如果使用线程编程,那么您的应用程序可能有多个堆栈,因为每个线程通常需要它自己的堆栈。构成此堆栈的内存通常以与堆区域分配内存的方式非常相似的方式分配,甚至可以使用堆分配函数。有时它会以不同的方式完成,以便堆栈可以动态增长。

答案 4 :(得分:2)

  

内存中的地址从最高到最低

您街道上房屋的地址是从最高到最低,还是从最低到最高?那么,这取决于你驾驶的方式。

就像邮政地址一样,内存地址根本就没有订购。每个地址只是识别内存中的唯一位置(至少在概念上。我们暂时忽略分段或虚拟内存)。

但是,当您的邮件运营商发送每日邮件时,他很可能以最高到最低或最低到最高的顺序工作(可能两者都在街道的一侧,而在另一侧)。当然,这比随意挨家挨户跳跃更有效。此外,它使承运人的工作更加简单。如果他以随机的顺序挨家挨户地跳,那么很难跟踪他已经访问过哪些房屋,以及哪些房屋仍然需要交付。如果他按顺序排列,那么他的卡车位置就是他需要跟踪的全部。

堆栈与此类似。它不占用内存中的任意位置,而是具有第一个位置,后续位置从那里按逻辑顺序跟随。通过这种方式,可以使用堆栈指针(通常为“SP”)来跟踪哪些堆栈位置被占用以及哪些堆栈位置是空闲的。

但是,堆必然是不同的。虽然堆栈固有地具有先进先出的排序,但堆本质上是无序的。堆内存可以随时分配和释放。早期的分配可以比以后的分配更长。因此,堆必须能够分配任意地址范围,并且必须全部跟踪它们。

由于堆栈和堆的运行方式不同,它们应该占用不同的内存区域。在您的示例中,第二个堆栈分配将覆盖堆分配占用的内存。显然,这将是一件坏事,而且被称为stack overflow

大多数现代CPU都具有保持堆栈内存和堆内存完全分离所需的所有功能。这是内存段和虚拟内存发挥作用的地方。在某些平台上,堆栈和堆可以由相同的地址范围标识,同时仍然占用物理内存的不同区域甚至是二级存储。讨论它是如何工作的,超出了本文的范围。

但是,大多数现代操作系统不会实际执行此操作。更常见的是,使用“平坦”地址空间,其中所有地址,无论是堆栈,堆,代码还是其他,都指向相同的地址空间。通过避免为每个地址处理段标识符的需要,这使应用程序开发人员更容易。

在扁平地址空间中,使用了相同的分离堆栈和堆的方案,这种方案在没有内存分段或虚拟化的古老CPU中使用:堆栈从内存的“顶部”(更高的地址)开始增长,并且堆从内存的底部(较低的地址)长大。两者之间的某一点可能被选为两者的限制,当一个点到达该点时,会发生错误情况 - 堆栈溢出内存不足

当然,这种描述是一个巨大的简化,但希望它能提供更好的基本理解。

答案 5 :(得分:1)

pointerType* pointer = new pointerType //creates memory address 0xffffff

你误解了0xffffff。它不是新分配的内存的地址 - 它是指针变量本身的地址。如果要检查分配内存的位置,则需要检查0xffffff

中存储的值

答案 6 :(得分:0)

指针本身和本地对象对象都位于堆栈中。指针指向的对象位于堆上。

这就是为什么在C中经常说只有价值传递。当你传递指向函数的指针时,传递一个int没有什么不同:函数参数的接收端的指针/ int是堆栈上的另一个“东西”。但是,由于指针的值(堆对象的地址)被复制到接收端,因此可以访问相同的堆对象。

答案 7 :(得分:0)

你总能,你知道,检查。这是你可以通过输出值并检查它来找到的东西之一。这并不保证在所有计算机或所有编译器上都是相同的行为,但您可以看到您的情况。只需printf(“0x%x \ n”,& variable)来查看变量的地址是什么。在堆上分配一些东西,在堆栈上分配一些东西,然后检查它们的所有地址,你会看到它们的位置以及每个方向的扩展方式。

我通常会在我使用的每种语言中保留一个测试项目,只是为了快速添加一些东西,以便自己查看它是如何工作的。这样做非常适合这种情况,并且您得到的答案甚至比StackOverflow可以提供的更快。

答案 8 :(得分:0)

堆栈和堆不是互斥的。想象一下这两个假设的幕后编译器实现:

void PreMain()
{
    char initialHeap[initialHeapSize]
    HeapPointer heapHead = &initialHeap;
    ...
    int returncode = main(argc, argv);
    ...
}

void PreMain()
{
    void * stack = GetFromOSHeap(stackSize);
    // some assembly intrinsic to replace the stack pointer
    ...
    int returncode = main(argc, argv);
    ...
}

你可能在主流编译器中找不到这些,但我敢打赌,这里有一个嵌入式系统,就像这样。