Почему вы хотите выделить память в куче, а не в стеке? [дубликат]

Возможные Дубликаты:
когда лучше использовать стек вместо кучи, и наоборот?

Я прочитал несколько других вопросов, касающихся кучи против стека, но они, похоже, больше сосредоточены на том, что делает куча/стек, а не на том, почему вы их используете.

Мне кажется, что распределение стека почти всегда будет предпочтительным, так как это быстрее (просто перемещение указателя стека против поиска свободного места в куче), и вам не нужно вручную освобождать выделенную память, когда вы закончите ее использовать. Единственная причина, по которой я могу видеть использование выделения кучи, - это если вы хотите создать объект в функции, а затем использовать его вне этой области функций, так как выделенная память стека автоматически освобождается после возвращения из функции.

существуют ли другие причины использования распределения кучи вместо распределения стека, о которых я не знаю?

9 ответов


есть несколько причин:

  • главное, что с выделением кучи, у вас есть самый гибкий контроль за временем жизни объекта (с malloc/calloc to free);
  • пространство стека обычно является более ограниченным ресурсом, чем пространство кучи, по крайней мере, в конфигурациях по умолчанию;
  • неспособность выделить пространство кучи может быть обработана изящно, в то время как исчерпание пространства стека часто невозможно восстановить.

без гибкое время жизни объекта, полезные структуры данных, такие как двоичные деревья и связанные списки, практически невозможно записать.


  1. вы хотите, чтобы распределение жило за пределами вызова функции
  2. вы хотите сохранить пространство стека (которое обычно ограничено несколькими Мб)
  3. вы работаете с повторно локализуемой памятью (Win16, базы данных и т. д.), или хотите восстановить после сбоев распределения.
  4. переменной длины ничего. Вы можете подделать это, но ваш код будет очень неприятным.

большой #1. Как только вы попадете в какой-либо параллелизм или IPC #1 везде. Даже большинство нетривиальных однопоточных приложений сложно разработать без некоторого распределения кучи. Это было бы практически подделкой функционального языка на C / C++.


поэтому я хочу сделать строку. Я могу сделать это на куче или на стопке. Давайте попробуем так:

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];

наиболее очевидным обоснованием использования кучи является вызов функции и необходимость возврата чего-то неизвестной длины. Иногда вызывающий может передать блок памяти и размер функции, но в других случаях это просто непрактично, особенно если возвращаемый материал сложен (например, коллекция различных объектов с указателями, летающими вокруг и т. д.).


ограничения размера являются огромным dealbreaker во многих случаях. Стек обычно измеряется в низких мегабайтах или даже килобайтах (это для все в стеке), тогда как все современные ПК позволяют вам несколько гигабайт кучи. Поэтому, если вы собираетесь использовать большой объем данных,вам абсолютно нужна куча.


добавить вы можете использовать alloca для выделения памяти в стеке, но опять же память в стеке ограничена, а также пространство существует только во время выполнения функции. это не означает, что все должно быть выделено в куче. как все дизайнерские решения это также несколько сложно, следует использовать "разумную" комбинацию обоих.


помимо ручного управления временем жизни объекта (о котором вы упомянули), другие причины использования кучи будут включать:

  • управление размером объекта во время выполнения (как начальный размер, так и "более поздний" размер во время выполнения программы).

например, можно выделить массив определенного размера, который известен только во время выполнения.

с введением VLA (массивов переменной длины) в C99 стало возможным выделить массивы фиксированный размер времени выполнения без использования кучи (это в основном языковая реализация функциональности "alloca"). Однако, в других случаях вам все равно понадобится куча даже в C99.

  • контроль времени выполнения над общим количеством объектов.

например, при построении структуры двоичного дерева вы не можете заранее выделить узлы дерева в стеке. Вы должны использовать кучу, чтобы выделить их "по требованию".

  • низкий уровень технические факторы, как ограниченное пространство стека (другие уже упоминали об этом).

когда вам нужен большой, скажем, буфер ввода-вывода, даже на короткое время (внутри одной функции) имеет смысл запросить его из кучи вместо объявления большого автоматического массива.


переменные стека (часто называемые "автоматическими переменными") лучше всего использовать для вещей, которые вы хотите всегда быть одинаковыми и всегда быть маленькими.

int x;
char foo[32];

все распределения стека, они также фиксируются во время компиляции.

лучшая причина для распределения кучи заключается в том, что вы не всегда знаете, сколько места вам нужно. Вы часто знаете это только после запуска программы. У вас может быть представление о пределах, но вы хотите использовать только точное количество пространства требуемый.

Если бы вам пришлось читать в файле, который может быть от 1k до 50mb, вы бы этого не сделали: -

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

это попытается выделить 50 МБ в стеке, который обычно терпит неудачу, поскольку стек обычно ограничен 256k в любом случае.


стек и куча разделяют одно и то же" открытое " пространство памяти и должны будут в конечном итоге прийти к точке, где они встречаются, если вы используете весь сегмент памяти. Сохранение баланса между пространством, которое каждый из них использует, будет иметь амортизированную стоимость позже для выделения и де-выделения памяти меньшего асимптотического значения.