堆地址是不是共享堆栈地址?

Posted

技术标签:

【中文标题】堆地址是不是共享堆栈地址?【英文标题】:Is stack address shared by Heap addresses?堆地址是否共享堆栈地址? 【发布时间】:2010-06-18 15:14:21 【问题描述】:

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

如果我创造...

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

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

localObject object

localObjects 地址会是 0xffffffe

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

【问题讨论】:

【参考方案1】:

内存中的地址从高到低开始

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

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

但是,当您的邮递员递送每日邮件时,他很可能会按照从高到低或从低到高的顺序(可能同时在街道的一侧和另一侧向上)工作。当然,这比随机地从一个房子跳到另一个房子更有效。此外,它使承运人的工作更加简单。如果他以随机顺序从一个家跳到另一个家,就很难跟踪他已经去过哪些房子,哪些还需要送货。如果他只是按顺序前进,那么他只需要跟踪卡车的位置。

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

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

由于栈和堆的操作方式不同,它们应该占用不同的内存区域。在您的示例中,第二个堆栈分配将覆盖您的堆分配占用的内存。显然,这将是一件坏事,这就是所谓的stack overflow。

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

但大多数现代操作系统并没有实际上这样做。更常见的是,使用“平面”地址空间,其中所有地址,无论是堆栈、堆、代码还是其他地址,都引用相同的地址空间。这使得应用程序开发人员更容易,因为不需要为每个地址处理段标识符。

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

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

【讨论】:

【参考方案2】:

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

【讨论】:

所以这意味着如果它们位于内存中的 2 个不同位置,则任何 2 个变量的地址都可能相同?? @numerical25,意思正好相反。没有两个变量可以有相同的地址因为它们在内存中是两个不同的地方。【参考方案3】:

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

堆栈和堆(在 C++ 标准中称为动态存储)的工作原理完全由实现定义,不受 C++ 标准的约束。

【讨论】:

【参考方案4】:

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

然而,在 DSP 上(例如),您通常有两个或三个完全独立的地址空间 - 例如,您的代码和数据将具有完全独立的 物理 寻址(即,一个一组内存芯片连接到一条内存总线用于数据,另一组单独的芯片连接到单独的总线用于代码)。在这种情况下,一个物理地址可以指代内存的两个独立部分中的任何一个。在不知道它所指的种类事物的情况下,该地址可能根本无法让您区分两者。

【讨论】:

【参考方案5】:

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

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

这有点像谎言,因为 x86 处理器支持分段,这会让人困惑,但这是关于计算机的使用方式,而不是严格来说它们支持什么。

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

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

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

堆是什么

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

真正的堆栈是什么

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

为什么这通常是台式机上的谎言

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

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

【参考方案8】:

你知道,你可以随时检查。这是您可以通过输出值并检查它来发现的事情之一。这不能保证在所有计算机或所有编译器上都是相同的行为,但您可以看到适合您的情况。只需 printf("0x%x\n", &variable) 即可查看变量的地址是什么。在堆上分配一些东西,在堆栈上分配一些东西,然后检查它们的所有地址,你会看到两者都在哪里,以及它们各自的延伸方向。

我通常会用我使用的每种语言准备一个测试项目,只是为了快速向其中添加一些东西,以亲眼看看它是如何工作的。这样做非常适合这种情况,而且您得到答案的速度甚至比 *** 提供的还要快。

【讨论】:

是的,但是在不了解具体的硬件架构和操作系统的情况下,您可能会根据这种经验测试得出错误的结论。 @BobbyShaftoe 我不知道怎么做。我提到了这样一个事实,即规则可能因环境而异,但只要您进行大量测试并获得一致的结果,那么您就知道您的环境是如何做到的。是的,您需要记住它在另一个环境中可能会有所不同,但通常您可以边做边学,只要您记住可能没有官方规范。如果您担心是否存在,那是一个不同的、密切相关的问题。如果 Q 是“我测试并得到了这个,是这样指定的吗?”,我们可以直接说“不”。 一种方法是考虑@nategoose 的答案。如果你在一个非常不同的架构中并且不知道它的基本原理,那么这种实验不会告诉你太多,甚至可能会让你感到困惑。这是我的基本观点。【参考方案9】:

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

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);
    ...

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

【讨论】:

以上是关于堆地址是不是共享堆栈地址?的主要内容,如果未能解决你的问题,请参考以下文章

进程和线程的区别

java中堆和栈的区别!!!!

进程地址空间中的共享内存?

怎么解决:无法启动INTERNET连接共享。一个已经用IP地址配置的LAN连接需要自动IP地址

Linux (x86) Exploit 开发系列教程之六(绕过ASLR - 第一部分)

internet 连接共享访问被启用时 一个已经用IP 地址配置的Lan连接需要自动IP地址