为什么你想要在堆而不是堆栈上分配内存?

时间:2009-10-11 05:37:31

标签: c memory memory-management stack heap

  

可能重复:
  When is it best to use a Stack instead of a Heap and vice versa?

我已经阅读了有关堆栈与堆栈的其他一些问题,但它们似乎更关注堆/堆栈的作用而不是使用它们的原因。

在我看来,堆栈分配几乎总是首选,因为它更快(只需移动堆栈指针而不是在堆中查找空闲空间),并且您不必手动释放已分配的内存完成后使用它。我可以看到使用堆分配的唯一原因是,如果您想在函数中创建一个对象,然后在该函数作用域之外使用它,因为堆栈分配的内存在从函数返回后自动取消分配。

使用堆分配而不是堆栈分配还有其他原因我不知道吗?

9 个答案:

答案 0 :(得分:37)

有几个原因:

  • 主要的一点是,通过堆分配,您可以最灵活地控制对象的生命周期(从malloc / callocfree);
  • 堆栈空间通常是比堆空间更有限的资源,至少在默认配置中是这样;
  • 无法正常处理堆空间分配,而堆栈空间不足则无法恢复。

如果没有灵活的对象生命周期,那么有用的数据结构(如二叉树和链表)几乎不可能写入。

答案 1 :(得分:19)

  1. 您希望分配超出函数调用
  2. 您希望节省堆栈空间(通常限制为几MB)
  3. 您正在使用可重新定位的内存(Win16,数据库等),或者想要从分配失败中恢复。
  4. 可变长度。你可以假装这个,但你的代码真的很讨厌。
  5. 最重要的是#1。一旦你进入任何类型的并发或IPC#1到处都是。即使大多数非平凡的单线程应用程序在没有一些堆分配的情况下设计也很棘手。这实际上是用C / C ++伪造一种函数式语言。

答案 2 :(得分:8)

所以我想制作一个字符串。我可以在堆上或堆栈上创建它。让我们试试两个:

char *heap = malloc(14);
if(heap == NULL)
  {
    // bad things happened!
  }
strcat(heap, "Hello, world!");

对于筹码:

char stack[] = "Hello, world!";

所以现在我在各自的地方都有这两个字符串。后来,我想让它们更长一些:

char *tmp = realloc(heap, 20);
if(tmp == NULL)
  {
    // bad things happened!
  }
heap = tmp;
memmove(heap + 13, heap + 7);
memcpy(heap + 7, "cruel ", 6);

对于筹码:

// umm... What?

这只是一个好处,其他人已经提到了其他好处,但这是一个相当不错的好处。使用堆,我们至少可以尝试使分配的空间更大。随着堆栈,我们坚持我们拥有的东西。如果我们想要增长空间,我们必须事先宣布这一切,我们都知道如何看到这一点:

char username[MAX_BUF_SIZE];

答案 3 :(得分:4)

使用堆的最明显的理由是当你调用一个函数并且需要返回未知长度的东西时。有时调用者可以将内存块和大小传递给函数,但在其他时候这是不切实际的,特别是如果返回的东西很复杂(例如,指针飞来飞去的不同对象的集合等)。

答案 4 :(得分:3)

在很多情况下,尺寸限制是一个巨大的破坏者。堆栈通常以低兆字节甚至千字节(对于堆栈中的所有)进行测量,而所有现代PC都允许您使用几千兆字节的堆。因此,如果您要使用大量数据,则绝对需要堆。

答案 5 :(得分:0)

只是添加 您可以使用alloca在堆栈上分配内存,但堆栈上的内存也是有限的,并且该空间仅在函数执行期间存在。 这并不意味着应该在堆上分配所有内容。像所有design decisions一样,这也有点困难,应该使用两者的“明智”组合。

答案 6 :(得分:0)

除了手动控制对象的生命周期(你提到过)之外,使用堆的其他原因包括:

  • 运行时控制对象的大小(在程序执行期间,初始大小和“稍后”大小)。

例如,您可以分配一定大小的数组,该数组仅在运行时已知。

随着在C99中引入VLA(可变长度数组),可以在不使用堆的情况下分配固定运行时大小的数组(这基本上是'alloca'功能的语言级实现)。但是,在其他情况下,即使在C99中你仍然需要堆。

  • 运行时控制对象总数。

例如,当您构建二叉树结构时,您无法事先在堆栈上有意义地分配树的节点。你必须使用堆来“按需”分配它们。

  • 低级技术考虑,因为有限的堆栈空间(其他人已经提到过)。

当你需要一个较大的I / O缓冲区时,即使是很短的时间(在单个函数内),从堆中请求它而不是声明一个大的自动数组更有意义。

答案 7 :(得分:0)

堆栈变量(通常称为“自动变量”)最适用于您希望始终相同的事物,并且始终很小。

int x;
char foo[32];

是否所有堆栈分配,这些在编译时也是固定的。

堆分配的最佳理由是您无法始终知道需要多少空间。一旦程序运行,您通常只知道这一点。您可能对限制有所了解,但您只想使用所需的确切空间量。

如果您必须阅读的文件可能是1k到50mb,那么您就不会这样做: -

int readdata ( FILE * f ) {
  char inputdata[50*1024*1025];
  ...
  return x;
}

那会尝试在堆栈上分配50mb,这通常会失败,因为堆栈通常限制为256k。

答案 8 :(得分:0)

堆栈和堆共享相同的“开放”内存空间,如果使用整个内存段,它们最终必须达到它们相遇的程度。保持每个人使用的空间之间的平衡将在以后分配和分配内存的成本进行摊销成为一个较小的渐近值。