Я занимаюсь программированием некоторое время, но в основном это были Java и C #. На самом деле мне никогда не приходилось управлять памятью самостоятельно. Я недавно начал программировать на C ++, и я немного не понимаю, когда мне следует хранить вещи в стеке, а когда - в куче.
Я понимаю, что переменные, к которым осуществляется очень частый доступ, должны храниться в стеке, а объекты, редко используемые переменные и большие структуры данных должны храниться в куче. Это правильно или я не прав?
Ответы:
Нет, разница между стеком и кучей не в производительности. Это срок службы: любая локальная переменная внутри функции (все, что вы не используете malloc () или new) живет в стеке. Он уходит, когда вы возвращаетесь из функции. Если вы хотите, чтобы что-то проживало дольше, чем функция, которая его объявила, вы должны разместить это в куче.
Для более четкого понимания того, что такое стек, подойдите к нему с другого конца - вместо того, чтобы пытаться понять, что делает стек с точки зрения языка высокого уровня, найдите «стек вызовов» и «соглашение о вызовах» и посмотрите, что машина действительно делает это, когда вы вызываете функцию. Компьютерная память - это просто набор адресов; «куча» и «стек» - изобретения компилятора.
источник
Я бы сказал:
Сохраните его в стеке, если МОЖЕТЕ.
Если НЕОБХОДИМО, храните его в куче.
Поэтому предпочитайте стек куче. Вот несколько возможных причин, по которым вы не можете хранить что-либо в стеке:
С помощью разумных компиляторов можно размещать объекты нефиксированного размера в куче (обычно это массивы, размер которых неизвестен во время компиляции).
источник
Это более тонко, чем предполагают другие ответы. Нет абсолютного разделения между данными в стеке и данными в куче в зависимости от того, как вы их объявляете. Например:
В теле функции, которая объявляет
vector
(динамический массив) из десяти целых чисел в стеке. Но хранилище, которым управляет,vector
отсутствует в стеке.Ах, но (другие ответы предполагают) время жизни этого хранилища ограничено временем жизни самого
vector
себя, которое здесь основано на стеке, поэтому не имеет значения, как оно реализовано - мы можем рассматривать его только как объект на основе стека с семантикой значений.Не так. Предположим, функция была:
Таким образом, все, что
swap
связано с функцией (а любой сложный тип значения должен иметь ее), может служить своего рода повторно привязанной ссылкой на некоторые данные кучи в системе, которая гарантирует единственного владельца этих данных.Поэтому современный подход C ++ заключается в том, чтобы никогда не хранить адрес данных кучи в переменных голых локальных указателей. Все выделения кучи должны быть скрыты внутри классов.
Если вы это сделаете, вы можете думать обо всех переменных в своей программе, как если бы они были простыми типами значений, и вообще забыть о куче (кроме случаев написания нового класса-оболочки, подобного значению, для некоторых данных кучи, что должно быть необычным) ,
Вам просто нужно сохранить одну особую информацию, которая поможет вам оптимизировать: где это возможно, вместо того, чтобы назначать одну переменную другой, как это:
поменяйте их местами так:
потому что это намного быстрее и не вызывает исключений. Единственное требование состоит в том, что вам не нужно
b
продолжать хранить то же самое значение (a
вместо этого оно получит значение, которое будет потеряноa = b
).Обратной стороной является то, что этот подход заставляет вас возвращать значения из функций через выходные параметры вместо фактического возвращаемого значения. Но они исправляют это в C ++ 0x с помощью ссылок rvalue .
В самых сложных ситуациях вы довели бы эту идею до крайности и использовали бы класс интеллектуального указателя, такой как тот,
shared_ptr
который уже находится в tr1. (Хотя я бы сказал, что если вам это нужно, вы, возможно, вышли за пределы сладкого места применимости Стандартного C ++.)источник
Вы также должны сохранить элемент в куче, если его нужно использовать за пределами функции, в которой он создан. Одна идиома, используемая с объектами стека, называется RAII - это включает использование объекта на основе стека в качестве оболочки для ресурса, когда объект уничтожается, ресурс будет очищен. Объекты на основе стека легче отслеживать, когда вы можете генерировать исключения - вам не нужно беспокоиться об удалении объекта на основе кучи в обработчике исключений. Вот почему необработанные указатели обычно не используются в современном C ++, вы должны использовать интеллектуальный указатель, который может быть оболочкой на основе стека для необработанного указателя на объект на основе кучи.
источник
Чтобы добавить к другим ответам, это также может быть связано с производительностью, по крайней мере, немного. Не то чтобы вам стоит об этом беспокоиться, если это не актуально для вас, но:
Выделение в куче требует отслеживания блока памяти, что не является операцией с постоянным временем (и требует некоторых циклов и накладных расходов). Это может замедлиться, поскольку память становится фрагментированной и / или вы приближаетесь к использованию 100% вашего адресного пространства. С другой стороны, выделение стека - это постоянное время, в основном «свободные» операции.
Еще одна вещь, которую следует учитывать (опять же, действительно важно, только если это становится проблемой), заключается в том, что обычно размер стека является фиксированным и может быть намного меньше размера кучи. Поэтому, если вы выделяете большие объекты или много мелких объектов, вы, вероятно, захотите использовать кучу; если у вас закончится место в стеке, среда выполнения выдаст исключительное исключение сайта. Обычно это не имеет большого значения, но стоит подумать о другом.
источник
Стек более эффективен и проще в управлении данными с заданной областью.
Но кучу следует использовать для всего, что превышает несколько КБ (в C ++ это просто, просто создайте
boost::scoped_ptr
в стеке указатель на выделенную память).Рассмотрим рекурсивный алгоритм, который постоянно обращается к себе. Очень сложно ограничить или угадать общее использование стека! В то время как в куче распределитель (
malloc()
илиnew
) может указывать на нехватку памяти, возвращаяNULL
илиthrow
ing.Источник : ядро Linux, размер стека которого не превышает 8 КБ!
источник
std::unique_ptr
, что должно быть предпочтительнее любой внешней библиотеки, такой как Boost (хотя со временем это все-таки приводит к стандарту).Для полноты картины вы можете прочитать статью Миро Самека о проблемах использования кучи в контексте встроенного ПО .
Куча проблем
источник
Выбор того, следует ли выделить в куче или в стеке, делается за вас, в зависимости от того, как выделяется ваша переменная. Если вы выделяете что-то динамически, используя «новый» вызов, вы распределяете его из кучи. Если вы выделяете что-то как глобальную переменную или как параметр в функции, оно размещается в стеке.
источник
На мой взгляд, есть два решающих фактора
Я бы предпочел использовать стек в большинстве случаев, но если вам нужен доступ к переменной вне области видимости, вы можете использовать кучу.
Чтобы повысить производительность при использовании кучи, вы также можете использовать функцию создания блока кучи, что может помочь в повышении производительности, а не размещении каждой переменной в разных местах памяти.
источник
вероятно, на это был дан довольно хороший ответ. Я хотел бы указать вам на приведенную ниже серию статей, чтобы лучше понять низкоуровневые детали. У Алекса Дарби есть серия статей, в которых он знакомит вас с отладчиком. Вот часть 3 о стеке. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/
источник