【问题标题】:What is the purpose of each of the memory locations, stack, heap, etc? (lost in technicalities)每个内存位置、堆栈、堆等的用途是什么? (迷失在技术上)
【发布时间】:2013-09-27 08:39:21
【问题描述】:

好的,我昨天问了 Stackoverflow 和 bufferoverflow 之间的区别,差点被否决而被遗忘,没有新信息。

所以这让我开始思考,我决定重新表述我的问题,希望得到真正解决我问题的答复。

所以这里什么都没有。

我知道四个内存段(如果我错了,请纠正我)。代码、数据、堆栈和堆。现在AFAIK代码段存储代码,而数据段存储与程序相关的数据。让我严重困惑的是堆栈和堆的用途!

据我了解,当您运行一个函数时,该函数的所有相关数据都存储在堆栈中,当您在函数内部递归调用函数时,函数内部......当函数等待时在前一个函数的输出上,该函数及其必要的数据不会从堆栈中弹出。所以你最终会出现堆栈溢出。 (如果我错了,请再次纠正我)

我也知道堆的用途。正如我在某处所读到的,它用于在程序执行时动态分配数据。但这提出了更多解决我问题的问题。当我最初在代码中初始化变量时会发生什么。它们是在代码段中还是在数据段中还是在堆中?数组存储在哪里?是不是在我的代码执行后,我堆中的所有内容都被删除了?总而言之,请以更简单的方式告诉我堆,而不仅仅是 malloc 和 alloc,因为我不确定我完全理解这些术语是什么!

我希望人们在回答时不要迷失在技术细节中,并且可以使术语保持简单以便外行人理解(即使要描述的概念不是外行人)并继续用技术术语教育我们,因为我们附和。我也希望这不是一个太大的问题,因为我真的认为它们不能分开问!

【问题讨论】:

标签: memory


【解决方案1】:

堆栈是干什么用的?

每个程序都由函数/子例程/无论您选择的语言如何称呼它们组成。几乎总是,这些函数都有一些本地状态。即使在一个简单的 for 循环中,您也需要在某个地方跟踪循环计数器,对吗?这必须存储在内存中的某个地方。

关于函数的事情是它们几乎总是做的另一件事是调用其他函数。那些其他函数有它们自己的局部状态——它们的局部变量。您不希望本地变量干扰调用者中的本地变量。必须发生的另一件事是,当 FunctionA 调用 FunctionB 然后必须执行其他操作时,您希望 FunctionA 中的局部变量仍然存在,并且在 FunctionB 完成时具有相同的值。

跟踪这些局部变量是堆栈的用途。每个函数调用都是通过设置所谓的堆栈帧来完成的。堆栈帧通常包括调用者的返回地址(用于函数完成时)、任何方法参数的值以及任何局部变量的存储。

当调用第二个函数时,会创建一个新的堆栈帧,将其压入堆栈顶部,然后调用就会发生。新函数可以愉快地处理它的堆栈帧。当第二个函数返回时,它的堆栈框架被弹出(从堆栈中移除)并且调用者的框架像以前一样回到原位。

这就是堆栈。那么堆是什么?它有类似的用途——存储数据的地方。但是,通常需要比单个堆栈帧寿命更长的数据。它不能进入​​堆栈,因为当函数调用返回时,它的堆栈框架被清理并繁荣 - 你的数据在那里。所以你把它放在堆上。堆是一个基本上非结构化的内存块。你要求 x 个字节,你得到它,然后就可以参与其中。在 C / C++ 中,堆内存保持分配状态,直到您显式取消分配。在垃圾回收语言(Java/C#/Python/等)中,堆内存中的对象不再使用时将被释放。

从上面解决您的具体问题:

栈溢出和缓冲区溢出有什么区别?

它们都是超出内存限制的情况。堆栈溢出是特定于堆栈的;您已经编写了代码(递归是一个常见的原因,但不是唯一的原因),因此它有太多的嵌套函数调用,或者您在堆栈上存储了很多大的东西,并且空间不足。大多数操作系统都对堆栈可以达到的最大大小进行了限制,当您达到该限制时,您会遇到堆栈溢出。现代硬件可以检测到堆栈溢出,这通常对您的进程来说是厄运。

缓冲区溢出有点不同。所以第一个问题 - 什么是缓冲区?嗯,这是一块有限的内存。该内存可能在堆上,也可能在堆栈上。但重要的是你知道你可以访问 X 个字节。然后,您编写一些代码,将 X + 更多字节写入该空间。编译器可能已经将缓冲区之外的空间用于其他内容,并且由于写入过多,您已经覆盖了其他内容。缓冲区溢出通常不会立即看到,因为直到您尝试对已被丢弃的其他内存执行某些操作时才会注意到它们。

另外,还记得我提到过返回地址也存储在堆栈中吗?这是由于缓冲区溢出导致的许多安全问题的根源。您的代码使用堆栈上的缓冲区并且存在溢出漏洞。聪明的黑客可以构建溢出缓冲区的数据以覆盖返回地址,指向缓冲区本身的代码,这就是他们获取代码执行的方式。太恶心了。

当我最初在代码中初始化变量时会发生什么。它们是在代码段中还是在数据段中还是在堆中?

我将在这里从 C/C++ 的角度进行讨论。假设你有一个变量声明:

int i;

这会在堆栈上保留(通常)四个字节。如果你有:

char *buffer = malloc(100);

这实际上保留了两块内存。对 malloc 的调用在堆上分配 100 个字节。但是您还需要存储指针缓冲区。同样,该存储在堆栈上,在 32 位机器上将是 4 个字节(64 位机器将使用 8 个字节)。

数组存储在哪里...???

这取决于您如何声明它们。如果你做一个简单的数组:

char str[128];

例如,这将在堆栈上保留 128 个字节。除非您通过调用诸如 malloc 之类的分配方法明确要求它,否则 C 永远不会命中堆。

如果您声明一个指针(如上面的缓冲区),则 指针 的存储在堆栈上,数组的实际数据在堆上。

是不是在我的代码执行后,我堆中的所有内容都被删除了...???

基本上,是的。操作系统将在进程退出后清理进程使用的内存。堆是进程中的一块内存,因此操作系统会清理它。尽管这取决于您所说的“清理它”是什么意思。操作系统将这些 RAM 块标记为现在空闲,并将在以后重用它。如果您有明确的清理代码(如 C++ 析构函数),您需要确保它们被调用,操作系统不会为您调用它们。

总而言之,请以更简单的方式告诉我堆,而不是仅仅用于 malloc 和 alloc?

堆就像它的名字一样,是一堆空闲字节,你可以一次抓取一块,做任何你想做的事情,然后扔回去做其他事情。你通过调用 malloc 获取一大块字节,然后通过调用 free 将其扔回。

你为什么要这样做?嗯,有几个常见的原因:

  1. 你不知道有多少东西 您需要直到运行时(基于 例如用户输入)。那么你 在堆上动态分配为 你需要它们。

  2. 您需要大型数据结构。上 Windows,例如,一个线程的 堆栈默认限制为 1 梅格。如果您正在使用大型 位图,例如,这将是 快速炸掉你的筹码并获得 堆栈溢出。所以你抓住那个 堆的空间,通常是 比堆栈大得多。

代码、数据、栈和堆?

不是一个真正的问题,但我想澄清一下。 “代码”段包含应用程序的可执行字节。通常,代码段仅在内存中读取,以帮助防止篡改。数据段包含编译到代码中的常量 - 诸如代码中的字符串或数组初始化器之类的东西需要存储在某个地方,数据段就是它们所在的位置。同样,数据段通常是只读的。

堆栈是内存的可写部分,通常具有有限的大小。操作系统将初始化堆栈,C 启动代码会为您调用 main() 函数。堆也是内存的可写部分。它由操作系统保留,诸如 malloc 和 free 之类的功能管理从中取出块并将它们放回原处。

所以,这就是概述。我希望这会有所帮助。

【讨论】:

  • 全局变量是怎么处理的...不是存储在数据段吗?
  • 哇,这真是一篇长篇大论的文章,​​但它肯定会有所帮助……需要阅读几次才能完全理解,但这肯定会回答很多问题!当他们想到时,会在这里带着疑问回复:)谢谢。 +1
  • 全局变量的存储位置确实因操作系统、加载程序和编译器而异。 C 内存模型实际上并没有“数据段”的概念,它是链接器/操作系统加载器的一部分。它们可以存储在数据段中。它们可以存储在 main 堆栈帧之前的堆栈的最底部。它们可能在某处。出于您的应用程序的目的,您并不真正关心。
【解决方案2】:

关于堆栈...这正是存储函数/过程的参数和局部变量的地方。更准确地说,当前正在执行的函数的参数和局部变量只能从堆栈中访问......其他属于函数链的变量在它之前执行将在堆栈中,但直到当前函数才能访问完成了它的操作。

关于全局变量,我相信它们存储在数据段中,并且始终可以从创建的程序中的任何函数访问。

关于堆...这些是可以在您需要时分配给程序的额外内存(malloc 或 new)...您需要知道分配的内存在堆中的位置(地址/指针)以便您可以在需要时访问它。如果您丢失了地址,则内存将无法访问,但数据仍保留在那里。 取决于平台和语言,这必须由您的程序手动释放(或发生内存泄漏)或需要进行垃圾回收。 堆比较大,无法堆叠,因此可用于存储大量数据(如文件、流等)...这就是为什么在堆中创建对象/文件并将指向对象/文件的指针存储在堆栈中的原因。

【讨论】:

  • 谢谢,这是迄今为止我见过的最简单的方法:)顺便说一句,是否也存在堆溢出?
  • “堆溢出”通常称为“内存不足”。
【解决方案3】:

就 C/C++ 程序而言,数据段存储静态(全局)变量,堆栈存储局部变量,堆存储动态分配的变量(您可以通过 mallocnew 获取指向的任何变量) .代码段仅存储机器代码(由 CPU 执行的程序部分)。

【讨论】:

    猜你喜欢
    • 2014-07-26
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 2011-08-01
    相关资源
    最近更新 更多