【问题标题】:memory allocation in Stack and Heap堆栈和堆中的内存分配
【发布时间】:2011-10-09 21:07:27
【问题描述】:

这似乎是一个非常基本的问题,但它一直在我的脑海中:

当我们分配一个局部变量时,它会进入堆栈。类似地,动态分配会导致变量进入堆。现在,我的问题是,这个变量实际上是位于堆栈还是堆上,还是我们只是堆栈和堆中的一个引用。

例如,

假设我声明了一个变量int i。现在这个i 被分配在堆栈上。那么,当我打印i 的地址时,这将是堆栈上的位置之一吗?堆也是同样的问题。

【问题讨论】:

  • 我不太明白你的问题。但是你可以阅读Computer Systems - A Programmer's Perspective一书,你就会知道答案。

标签: c memory-management heap-memory dynamic-allocation stack-memory


【解决方案1】:

我不完全确定你在问什么,但我会尽力回答。

下面在栈上声明了一个变量i

int i;

当我使用&i 请求地址时,我得到了堆栈上的实际位置。

当我使用 malloc 动态分配某些内容时,实际上存储了 条数据。动态内存是在堆上分配的,而指针本身是在栈上分配的。所以在这段代码中:

int* j = malloc(sizeof(int));

这是在堆上为整数分配空间。它还在堆栈上为指针 (j) 分配空间。变量j的值设置为malloc返回的地址。

【讨论】:

  • 感谢克里斯的回答。这是我一直在寻找的答案。所以,这就是为什么我们会遇到程序用完堆栈但永远不会用完 HEAP 的问题,因为 HEAP 会受到系统内存的限制。
  • 实际上,程序如此快地耗尽堆栈空间的唯一原因是,通常的做法是对堆栈空间设置非常小的限制(我认为 8KB 很常见)。是的,如果你允许的话,堆会变得非常大。
  • @Samir 没有。堆栈和堆都受到系统内存量的限制。程序在用完堆之前用完堆栈,因为堆栈大小通常比堆小几个数量级。不过,程序仍然可以用完堆。
  • @Chris:在 Windows 上,限制通常是 1MB,而不是 8kB。我假设其他系统也有类似的限制。当然,这对于嵌入式系统来说可能非常不同。
  • @Rudy:我认为在 Windows 上,限制已编译为二进制文件,因此取决于开发人员。我绝对可以相信 1MB 是默认值,如果你问我,8KB 似乎很简陋......
【解决方案2】:

希望以下内容有所帮助:

void foo()
{
    // an integer stored on the stack
    int a_stack_integer; 

    // a pointer to integer data, the pointer itself is stored on the stack
    int *a_stack_pointer; 

    // make a_stack_pointer "point" to integer data that's allocated on the heap
    a_stack_pointer = (int*)malloc(10 * sizeof(int));
}

在堆栈变量的情况下,变量本身(实际数据)存储在堆栈中。

在堆分配内存的情况下,底层数据总是存储在堆上。指向此内存/数据的指针可能本地存储在堆栈中。

希望这会有所帮助。

【讨论】:

  • 这对 Darren 很有帮助,但你能向我解释一个场景,在堆分配内存的情况下,指针可能不会存储在堆栈上吗?
  • @Samir:您可能有一个更复杂的数据结构,其中堆分配的数据包含指向堆分配数据的其他段的指针。链表的传统实现就是一个例子,链表中的每个“节点”都包含一个指向下一个“节点”的指针,依此类推
【解决方案3】:

指针变量本身将驻留在堆栈中。指针指向的内存将驻留在堆上。

int *i = malloc(sizeof(int));

i 将驻留在堆栈上,我指向 *i 的实际内存将在堆上。

【讨论】:

    【解决方案4】:

    我同意克里斯的观点。只是另一种解释方式。考虑以下代码:

    int* j = malloc(sizeof(int));
    free(j);
    

    即使在使用 free(j) 应该从堆中释放内存之后,指针仍然存在,我们需要明确地将其设为 NULL。这肯定表明指针还有一个堆栈对应物,否则它应该在 free 命令之后不存在。这个堆栈变量是指向堆上使用 malloc 动态分配内存的地址的变量。

    【讨论】:

      【解决方案5】:

      先生。 Eberle 的答案是 100% 正确的,但是由于 Google 在搜索 malloc heap or stack 时将其显示为第一个答案,所以我必须补充一点,malloc() 大部分时间都在堆上分配数据。如果分配的数据大于MMAP_THRESHOLD(在 32 位系统上通常为 128kb),malloc()使用堆,而是将数据分配到 匿名内存段 通常位于堆栈下方,向内存不足的方向增长。

      这与动态加载的库所在的区域相同(libc.so 等)。这是来自man malloc的相关段落:

      通常,malloc() 从堆中分配内存,并调整 使用 sbrk(2) 根据需要设置堆的大小。分配块时 大于 MMAP_THRESHOLD 字节的内存,则 glibc malloc() 实现使用 mmap(2) 将内存分配为私有匿名映射。 MMAP_THRESHOLD 默认为 128 kB, 但可以使用 mallopt(3) 进行调整。之前 使用 mmap(2) 执行的 Linux 4.7 分配不受 RLIMIT_DATA 资源限制的影响;从 Linux 4.7 开始,这个限制也是 对使用 mmap(2) 执行的分配强制执行。

      作为一个实际示例,请随时查看following post。它基本上用malloc()分配了300kb,然后运行pmap <PID>来显示相关的内存段。

      【讨论】:

      • 很确定 MMAP_THRESHOLD 不是 ANSI/ISO C 或任何 POSIX 标准的一部分。仍然很有趣,但不是所有 C 实现的内在真理。看起来这对于 glibc 和 musl 来说是正确的。
      【解决方案6】:

      堆栈或堆不是独立的内存,它们是系统为正在运行的程序分配的内存段,只是在内存中组织数据的不同方式。

      所以当你得到 &i 时,它就是一个内存地址,就这么简单。

      【讨论】:

        猜你喜欢
        • 2012-12-05
        • 2019-05-17
        • 2017-01-04
        • 2011-05-28
        • 2021-11-18
        • 1970-01-01
        • 2018-07-24
        • 1970-01-01
        相关资源
        最近更新 更多