Зачем вам когда-либо хотеть выделять память на кучу, а не на стек?
Возможный дубликат:
Когда лучше использовать стек вместо кучи и наоборот?
Я прочитал несколько других вопросов относительно кучи и стека, но они, похоже, больше сосредотачиваются на том, что делает куча/стек, а не на том, почему вы их используете.
Мне кажется, что распределение стека почти всегда будет предпочтительным, поскольку оно быстрее (просто перемещение указателя стека и поиск свободного места в куче), и вам не нужно вручную освобождать выделенную память, когда вы сделано с его помощью. Единственная причина, по которой я могу понять, использовать выделение кучи, - это то, что вы хотите создать объект в функции, а затем использовать его вне этой области видимости, поскольку выделенная память в стеке автоматически не распределяется после возврата из функции.
Существуют ли другие причины использования распределения кучи вместо распределения стека, о котором я не знаю?
Ответы
Ответ 1
Есть несколько причин:
- Главное, что с распределением кучи у вас есть самый гибкий контроль над временем жизни объекта (от
malloc
/calloc
до free
);
- Пространство стека обычно является более ограниченным ресурсом, чем пространство с кучей, по крайней мере, в конфигурациях по умолчанию;
- Невозможность выделения кучного пространства может быть обработана изящно, в то время как неисполнение пространства стека часто невосстанавливается.
Без гибкого срока жизни объекта полезные структуры данных, такие как бинарные деревья и связанные списки, практически невозможно записать.
Ответ 2
- Вы хотите, чтобы распределение выходило за вызов функции
- Вы хотите сохранить пространство стека (которое обычно ограничено несколькими МБ).
- Вы работаете с повторно локалируемой памятью (Win16, базы данных и т.д.) или хотите восстановить отказ от размещения.
- Переменная длина. Вы можете подделывать это, но ваш код будет действительно неприятным.
Большой - # 1. Как только вы попадаете в какой-либо concurrency или IPС# 1, везде. Даже самые нетривиальные однопоточные приложения сложны для разработки без некоторого распределения кучи. Это было бы фактически фальсифицировать функциональный язык в C/С++.
Ответ 3
Итак, я хочу создать строку. Я могу сделать это в куче или в стеке. Попробуйте оба:
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];
Ответ 4
Наиболее очевидное обоснование использования кучи - это когда вы вызываете функцию и нуждаетесь в возврате неизвестной длины. Иногда вызывающий может передать блок памяти и размер функции, но в других случаях это просто непрактично, особенно если возвращаемый материал является сложным (например, коллекция различных объектов с указателями, летающими вокруг и т.д.).
Ответ 5
Ограничения по размеру - это большой разбойник во многих случаях. Стек обычно измеряется в мегабайтах или даже килобайтах (что для всего в стеке), тогда как все современные ПК позволяют вам несколько гигабайт кучи. Поэтому, если вы собираетесь использовать большой объем данных, вам абсолютно нужна куча.
Ответ 6
просто добавить
вы можете использовать alloca для выделения памяти в стеке, но опять же память в стеке ограничена, а также пространство существует только во время выполнения функции,
это не значит, что все должно быть выделено на кучу. как и все дизайнерские решения, это также несколько сложно, следует использовать "разумную" комбинацию обоих.
Ответ 7
Помимо ручного управления временем жизни объекта (которое вы упомянули), другие причины использования кучи будут включать в себя:
- Контроль времени выполнения объекта (как начального размера, так и размера "позже" во время выполнения программы).
Например, вы можете выделить массив определенного размера, который известен только во время выполнения.
С введением VLA (Variable Length Arrays) в C99 стало возможным выделять массивы фиксированного размера времени выполнения без использования кучи (это в основном реализация уровня "alloca" на уровне языка). Однако в других случаях вам все равно нужна куча даже на C99.
- Контроль времени выполнения всего количества объектов.
Например, когда вы строите двоичную структуру дерева, вы не можете значительно выделить узлы дерева в стеке заранее. Вы должны использовать кучу, чтобы выделить их "по требованию".
- Низкоуровневые технические соображения, как ограниченное пространство стека (другие уже упомянули об этом).
Если вам нужен большой, скажем, буфер ввода-вывода, даже на короткое время (внутри одной функции), имеет смысл запросить его из кучи вместо объявления большого автоматического массива.
Ответ 8
Переменные стека (часто называемые "автоматическими переменными" ) лучше всего использовать для вещей, которые вы всегда должны быть одинаковыми, и всегда быть маленькими.
int x;
char foo[32];
Являются ли все распределения стека, они также фиксируются во время компиляции.
Лучшая причина для распределения кучи заключается в том, что вы не можете всегда знать, сколько места вам нужно. Вы часто знаете об этом только после запуска программы. У вас может быть представление о ограничениях, но вы хотели бы использовать только то необходимое количество места.
Если вам нужно было прочитать в файле, который может быть от 1k до 50mb, вы не сделали бы этого: -
int readdata ( FILE * f ) {
char inputdata[50*1024*1025];
...
return x;
}
Это попытается выделить 50 МБ в стеке, который обычно терпит неудачу, так как стек обычно ограничен до 256 тыс. в любом случае.
Ответ 9
Стек и куча разделяют одно и то же "открытое" пространство памяти и должны будут в конечном итоге подойти к точке, где они встречаются, если вы используете весь сегмент памяти. Сохранение баланса между пространством, которое использует каждый из них, впоследствии будет амортизировано для распределения и де-распределения памяти меньшим асимптотическим значением.