Сегмент стека вызовов
Сегмент стека вызовов содержит память, используемую для стека вызовов. Когда приложение запускается, операционная система помещает в стек вызовов функцию . Затем программа начинает выполняться.
Когда встречается вызов функции, эта функция помещается в стек вызовов. Когда текущая функция завершается, эта функция удаляется из стека вызовов. Таким образом, глядя на функции, помещенные в стек вызовов, мы можем увидеть все функции, которые были вызваны для перехода к текущей точке выполнения.
Приведенная выше аналогия с почтовыми ящиками в значительной степени похожа на то, как работает стек вызовов. Сам стек представляет собой блок адресов памяти фиксированного размера. Почтовые ящики – это адреса памяти, а «элементы», которые мы помещаем в стек, называются кадрами (фреймами) стека. Кадр стека отслеживает все данные, связанные с одним вызовом функции. Мы поговорим о стековых кадрах чуть позже. «Маркер» – это регистр (небольшой фрагмент памяти в CPU), известный как указатель стека (иногда сокращенно «SP», «stack pointer»). Указатель стека отслеживает текущее положение вершины стека вызовов.
Мы можем сделать еще одну оптимизацию: когда мы извлекаем элемент из стека вызовов, нам нужно только переместить указатель стека вниз – нам не нужно очищать или обнулять память, используемую извлекаемым кадром стека (эквивалент опустошению почтового ящика). Эта память больше не считается «в стеке» (указатель стека будет по этому адресу или ниже), поэтому к ней не будет доступа. Если мы позже поместим новый кадр стека в ту же самую память, он перезапишет старое значение, которое мы никогда не очищали.
Как подобрать стек для проекта 0
- 09.06.20 10:06
•
VitaliSergey
•
#505912
•
Хабрахабр
•
В черновиках
•
•
7
Программирование, Управление проектами, Управление разработкой, Анализ и проектирование систем
При разработке любого приложения клиентам всегда важно получить результат, а разработчикам — найти самый оптимальный путь для достижения этого результата. Эффективность приложений зависит от множества факторов, важнейшим из которых является правильно подобранный стек технологий
В этой статье мы рассмотрим, что из себя представляет тех. стек, рассмотрим факторы, которые стоит учитывать при выборе стека, а также готовые решения для проектов. Если интересно, добро пожаловать под кат.
Коротко о стеке
Tech Stack — это набор средств для реализации задуманной IT идеи. Как правило, стек состоит из языков программирования, фреймворков, библиотек, различных инструментов для разработки и самих подходов к программированию. Синтез всего перечисленного предопределяет жизнеспособность и конкурентоспособность приложения, его функциональность, масштабируемость и дальнейшее обслуживание.
Тех. стек можно условно разделить на 2 части: клиентскую (front-end) и серверную (back-end).
Front-end технологии включают в себя:
- язык разметки HTML;
- таблицы стилей CSS;
- язык программирования JavaScript;
- UI-фреймворки и библиотеки: React, Angular, Node.js, jQuery и др.
Back-end технологии включают в себя:
- операционную систему;
- веб сервер: Nginx, Apache;
- языки программирования C#, Java, PHP, Python, Objective-C и др;
- различные фреймворки, надстроенные над языками программирования: Node.js, .NET, Django и др;
- базы данных: Microsoft SQL Server, MySQL, PostgreSQL, Oracle, Neo4j и др;
- облачные инфраструктуры и сервисы: AWS, Microsoft Azure, Google Cloud, Heroku и др.
Стек вызовов в действии
Давайте подробнее рассмотрим, как работает стек вызовов. Вот последовательность шагов, которые выполняются при вызове функции:
- Программа обнаруживает вызов функции.
- Кадр стека создается и помещается в стек. Кадр стека состоит из:
- Адрес инструкции, следующей после вызова функции (называемый адресом возврата). Таким образом, CPU запоминает, куда вернуться после выхода из вызываемой функции.
- Все аргументы функции.
- Память для любых локальных переменных.
- Сохраненные копии любых регистров, измененных функцией, которые необходимо восстановить после возврата из функции.
- CPU переходит к начальной точке функции.
- Инструкции внутри функции начинают выполняться.
Когда функция завершается, происходят следующие шаги:
- Регистры восстанавливаются из стека вызовов
- Кадр стека извлекается из стека. Это освобождает память для всех локальных переменных и аргументов.
- Обрабатывается возвращаемое значение.
- CPU возобновляет выполнение по адресу возврата.
Возвращаемые значения могут обрабатываться разными способами в зависимости от архитектуры компьютера. Некоторые архитектуры включают возвращаемое значение как часть кадра стека. Другие используют регистры CPU.
Обычно неважно знать все подробности о том, как работает стек вызовов. Однако понимание того, что функции помещаются в стек при их вызове и удаляются при возврате, дает вам основы, необходимые для понимания рекурсии, а также некоторые другие концепции, полезные при отладке
Техническое примечание: на некоторых архитектурах стек вызовов при увеличении изменяет адрес памяти в направлении от нуля. На других он при увеличении изменяет адрес в направлении нуля. Как следствие, новые добавленные кадры стека могут иметь более высокий или более низкий адрес памяти, чем предыдущие.
Массивы, коллекции, списки, очереди … Стек!
Часто люди задают вопрос: «Стек — что это такое?». «Программирование» и «систематизация» — интересные понятия: они не синонимы, но так тесно связаны. Программирование прошло очень быстро такой длительный путь, что достигнутые вершины кажутся идеальными. Скорее всего, это не так. Но очевидно другое.
Идея стека стала привычной не только на уровне различных языков программирования, но и на уровне их конструкций и возможностей по созданию типов данных. Любой массив имеет push и pop, а понятия «первый и последний элементы массива» стали традиционными. Раньше были просто элементы массива, а сегодня есть:
- элементы массива;
- первый элемент массива;
- последний элемент массива.
Операция помещения элемента в массив сдвигает указатель, а извлечение элемента с начала массива или с его конца имеет значение. По сути это тот же стек, но в применении к другим типам данных.
Особенно примечательно, что популярные языки программирования не имеют конструкции stack. Но они предоставляют его идею разработчику в полном объеме.
Стек: что это такое и применение на News4Auto.ru.
Наша жизнь состоит из будничных мелочей, которые так или иначе влияют на наше самочувствие, настроение и продуктивность. Не выспался — болит голова; выпил кофе, чтобы поправить ситуацию и взбодриться — стал раздражительным. Предусмотреть всё очень хочется, но никак не получается. Да ещё и вокруг все, как заведённые, дают советы: глютен в хлебе — не подходи, убьёт; шоколадка в кармане — прямой путь к выпадению зубов. Мы собираем самые популярные вопросов о здоровье, питании, заболеваниях и даем на них ответы, которые позволят чуть лучше понимать, что полезно для здоровья.
Куча
Сегмент кучи (или просто «куча») отслеживает память, используемую для динамического выделения. Мы уже немного поговорили о куче на уроке о динамическом выделении памяти в языке С++.
В языке C++ при использовании оператора new динамическая память выделяется из сегмента кучи самой программы:
int *ptr = new int; // для ptr выделяется 4 байта из кучи
int *array = new int; // для array выделяется 40 байт из кучи
1 2 |
int*ptr=newint;// для ptr выделяется 4 байта из кучи
int*array=newint10;// для array выделяется 40 байт из кучи |
Адрес выделяемой памяти передается обратно оператором new и затем он может быть сохранен в указателе. О механизме хранения и выделения свободной памяти нам сейчас беспокоиться незачем. Однако стоит знать, что последовательные запросы памяти не всегда приводят к выделению последовательных адресов памяти!
int *ptr1 = new int;
int *ptr2 = new int;
// ptr1 и ptr2 могут не иметь последовательных адресов памяти
1 2 3 |
int*ptr1=newint;
int*ptr2=newint; // ptr1 и ptr2 могут не иметь последовательных адресов памяти |
При удалении динамически выделенной переменной, память возвращается обратно в кучу и затем может быть переназначена (исходя из последующих запросов). Помните, что удаление указателя не удаляет переменную, а просто приводит к возврату памяти по этому адресу обратно в операционную систему.
Куча имеет свои преимущества и недостатки:
Выделение памяти в куче сравнительно медленное.
Выделенная память остается выделенной до тех пор, пока не будет освобождена (остерегайтесь утечек памяти) или пока программа не завершит свое выполнение.
Доступ к динамически выделенной памяти осуществляется только через указатель. Разыменование указателя происходит медленнее, чем доступ к переменной напрямую.
Поскольку куча представляет собой большой резервуар памяти, то именно она используется для выделения больших массивов, структур или классов.
Стек вызовов
В программировании есть два вида стека — стек вызовов и стек данных.
Когда в программе есть подпрограммы — процедуры и функции, — то компьютеру нужно помнить, где он прервался в основном коде, чтобы выполнить подпрограмму. После выполнения он должен вернуться обратно и продолжить выполнять основной код. При этом если подпрограмма возвращает какие-то данные, то их тоже нужно запомнить и передать в основной код.
Чтобы это реализовать, компьютер использует стек вызовов — специальную область памяти, где хранит данные о точках перехода между фрагментами кода.
Допустим, у нас есть программа, внутри которой есть три функции, причём одна из них внутри вызывает другую. Нарисуем, чтобы было понятнее:
Программа запускается, потом идёт вызов синей функции. Она выполняется, и программа продолжает с того места, где остановилась. Потом выполняется зелёная функция, которая вызывает красную. Пока красная не закончит работу, все остальные ждут. Как только красная закончилась — продолжается зелёная, а после её окончания программа продолжает свою работу с того же места.
А вот как стек помогает это реализовать на практике:
Программа дошла до синей функции, сохранила точку, куда ей вернуться после того, как закончится функция, и если функция вернёт какие-то данные, то программа тоже их получит. Когда синяя функция закончится и программа получит верхний элемент стека, он автоматически исчезнет. Стек снова пустой.
С зелёной функцией всё то же самое — в стек заносится точка возврата, и программа начинает выполнять зелёную функцию. Но внутри неё мы вызываем красную, и вот что происходит:
При вызове красной функции в стек помещается новый элемент с информацией о данных, точке возврата и указанием на следующий элемент. Это значит, что когда красная функция закончит работу, то компьютер возьмёт из стека адрес возврата и вернёт управление снова зелёной функции, а красный элемент исчезнет. Когда и зелёная закончит работу, то компьютер из стека возьмёт новый адрес возврата и продолжит работу со старого места.
Стек и стратегия кэш-игры
То количество денег, которое игрок берет с собой за кэш-стол, должно опираться прежде всего на планы по его стратегии на игру. Если у покериста от 20 до 40 ББ, то он вынужден играть по стратегии короткого стека. То есть его активность предполагает агрессивную игру на префлопе, так как на улицах постфлопа, имея малое количество средств, он уже не того маневра, который предполагает игра с полным стеком.
Поэтому бывалые игроки берут с собой за стол полный или даже глубокий стек. 100 ББ и больше помогут грамотно и квалифицированно проводить в игре тактические приемы, которые в конечном итоге оборачиваются прибылью.
Можно прийти к мнению, что игра с полным и глубоким стеком может привести к скорой потере части банкролла. Ведь, если кто-либо из оппонентов за столом выставиться и игрок с полным стеком ответит на олл-ин и проиграет ва-банк, его банкролл заметно просядет. Такое суждение справедливо, но только с точки зрения новичка в покере, для которого потеря стека в 100 ББ и больше видится катастрофой. Мы же говорим об опытных покеристах, которые придерживаются стратегии долгосрочной перспективы. Даже пара проигрышей полного стека в долгосрочной перспективе с высокой вероятностью окупится и в ровно такой же ситуации с олл-ином.
Поэтому важно докупать фишки между раздачами до полного стека. Этим игрок развязывает себе руки для осуществления покерных приемов и проведения собственной тактики
Если на каком-то этапе у вас стек снизился до уровня в 40 ББ, то лучше докупиться до полного. Тем более, что в онлайн покер-румах всегда есть функция автоматической докупки. Старайтесь играть правильно с первых дней. Пусть вы будете играть на низких бай-инах, но с полным стеком, чем на высоких с коротким. Поверьте, во втором случае банкролл будет таять намного быстрее, а опыта игры и навыков хорошего покериста вы не приобретете.
Но в некоторых ситуациях фишки необходимо сбрасывать. Это обязательно необходимо делать, если количество фишек в вашем стеке значительно превышает первоначальный показатель. Предположим, в игру на $0,1/$0,2 вы взяли с собой в качестве стека фишек на $20. То есть у вас полный стек. На каком-то этапе у вас скопилось $100.
В этой ситуации стоит быть осмотрительнее. Если у всех остальных игроков за столом стек по-прежнему в районе $20, можно продолжать играть. Если же у кого-либо из оппонентов стек также перешел из разряда полного в глубокий и более, то рекомендуем вам прерваться на время и скинуть излишек фишек, доведя стек до первоначального размера в $20.
Объясняется это довольно просто. Судите сами, если в ответ на агрессивный олл-ин оппонента со стеком в $120 вы ответите и проиграете свою сотню, то вы не сможете за один раз докупить проигранные фишки на $100. В игре стоит ограничение по докупке – $20. Следовательно, вы не сможете в полной мере реализовать математические ожидания от ситуации. Другими словами, впоследствии ваши $20 против его уже $220 на лимите $0,1/$0,2 так или иначе превратятся в пыль. К проигранным $100 добавятся еще $20.
А если вы вовремя сбросите выигранные $80, и останетесь в раздаче с $20, то, во-первых, сохраните выигрыш, к которому всегда можно будет вернуться, во-вторых, вернетесь в раздачу с полным стеком, который предоставляет вам все возможности реализовывать задуманные тактики. То есть не спешите переходить из разряда лидера в разряд догоняющего.
2015: Возможности и состав «Стек-Энерго»
«Стек-Энерго» — комплекс программ, предназначенный для автоматизированного решения бизнес-задач, стоящих перед энергосбытовывми, сетевыми компаниями и энергетическими холдингами.
Комплекс программ Стек-Энерго автоматизирует бизнес-процессы биллинга электрической энергии:
- Продажа электроэнергии потребителям – физическим лицам:
- учет лицевых счетов физических лиц;
- расчет объемов и стоимости отпущенной электрической энергии (в т.ч. на общедомовые нужды);
- формирование документов на оплату (счетов-квитанций);
- учет оплат, ведение сальдо по лицевым счетам.
- Продажа электроэнергии потребителям – юридическим лицам:
- учет договоров;
- расчет объемов и стоимости отпущенной электрической энергии (мощности);
- выставление первичных документов;
- учет оплат, ведение сальдо по договорам;
- формирование книги продаж и книги покупок.
- Работа с дебиторами физическими и юридическими лицами;
- Обслуживание абонентов по разным каналам взаимодействия: телефон, почта, СМС, интернет
- Формирование аналитической отчетности, сводной отчетности по предприятию и анализ накопленных в процессе сбытовой деятельности данных.
В состав комплекса входят модули:
Личный кабинет, Платежные сервисы, Мобильный контролер, СТЕК-Мониторинг, СМС-сервис, Электронный документооборот, Автообзвон, Разработка сайтов.
Схема взаимодействия в «Личном кабинете», 2014
Представление модуля «Мобильный контролер», 2014
Реализации[править]
Для стека с элементами требуется памяти, так как она нужна лишь для хранения самих элементов.
На массивеправить
Перед реализацией стека выделим ключевые поля:
- — массив, с помощью которого реализуется стек, способный вместить не более элементов,
- — индекс последнего помещенного в стек элемента.
Стек состоит из элементов , где — элемент на дне стека, а — элемент на его вершине.
Если , то стек не содержит ни одного элемента и является пустым (англ. empty). Протестировать стек на наличие в нем элементов можно с помощью операции — запроса . Если элемент снимается с пустого стека, говорят, что он опустошается (англ. underflow), что обычно приводит к ошибке. Если значение больше , то стек переполняется (англ. overflow)
(В представленном ниже псевдокоде возможное переполнение во внимание не принимается.)
Каждую операцию над стеком можно легко реализовать несколькими строками кода:
boolean empty(): return s.top == 0
function push(element : T): s.top = s.top + 1 s = element
T pop(): if empty() return error "underflow" else s.top = s.top - 1 return s
Как видно из псевдокода выше, все операции со стеком выполняются за .
На саморасширяющемся массивеправить
Возможна реализация стека на динамическом массиве, в результате чего появляется существенное преимущество над обычной реализацией: при операции push мы никогда не сможем выйти за границы массива, тем самым избежим ошибки исполнения.
Создадим вектор и определим операции стека на нём. В функции Перед тем, как добавить новый элемент, будем проверять, не нужно ли расширить массив вдвое, а в , перед тем, как изъять элемент из массива, — не нужно ли вдвое сузить размер вектора. Ниже приведён пример реализации на векторе.
Ключевые поля:
- — старый массив, в котором хранится стек,
- — временный массив, где хранятся элементы после перекопирования,
- — верхушка стека,
- — размер массива.
function push(element : T): if head == capacity - 1 T newStack for i = 0 to capacity - 1 newStack = s s = newStack capacity = capacity * 2 head++ s = element
T pop(): temp = s head-- if head < capacity / 4 T newStack[capacity / 2] for i = 0 to capacity / 4 - 1 newStack = s s = newStack capacity = capacity / 2 return temp
На спискеправить
Стек можно реализовать и на списке. Для этого необходимо создать список и операции работы стека на созданном списке. Ниже представлен пример реализации стека на односвязном списке. Стек будем «держать» за голову. Добавляться новые элементы посредством операции будут перед головой, сами при этом становясь новой головой, а элементом для изъятия из стека с помощью будет текущая голова. После вызова функции текущая голова уже станет старой и будет являться следующим элементом за добавленным, то есть ссылка на следующий элемент нового элемента будет указывать на старую голову. После вызова функции будет получена и возвращена информация, хранящаяся в текущей голове. Сама голова будет изъята из стека, а новой головой станет элемент, который следовал за изъятой головой.
Заведем конструктор вида
Ключевые поля:
- — значение в верхушке стека,
- — значение следующее за верхушкой стека.
function push(element : T): head = ListItem(head, element)
T pop(): data = head.data head = head.next return data
В реализации на списке, кроме самих данных, хранятся указатели на следующие элементы, которых столько же, сколько и элементов, то есть, так же . Стоит заметить, что стек требует дополнительной памяти на указатели в списке.
Суть и понятие стека
Процессор и память — основные конструктивные элементы компьютера. Процессор исполняет команды, манипулирует адресами памяти, извлекает и изменяет значения по этим адресам. На языке программирования все это трансформируется в переменные и их значения. Суть стека и понятие last in first out (LIFO) остается неизменным.
Аббревиатура LIFO уже не используется так часто, как раньше. Вероятно потому, что списки трансформировались в объекты, а очереди first in first out (FIFO) применяются по мере необходимости. Динамика типов данных потеряла свою актуальность в контексте описания переменных, но приобрела свою значимость на момент исполнения выражений: тип данного определяется в момент его использования, а до этого момента можно описывать что угодно и как угодно.
Так, стек — что это такое? Теперь вы знаете, что это вопрос неуместный. Ведь без стека нет современного программирования. Любой вызов функции — это передача параметров и адреса возврата. Функция может вызвать другую функцию — это опять передача параметров и адреса возврата. Наладить механизм вызова значений без стека — это лишняя работа, хотя достижимое решение, безусловно, возможное.
Многие спрашивают: «Стек — что это такое?». В контексте вызова функции он состоит из трех действий:
- сохранения адреса возврата;
- сохранения всех передаваемых переменных или адреса на них;
- вызова функции.
Как только вызванная функция исполнит свою миссию, она просто вернет управление по адресу возврата. Функция может вызывать любое количество других функций, так как ограничение накладывается только размером стека.
Использовать
Обработка звонков на сайт
Обычно манипуляции со стеком вызовов, необходимые на месте вызова подпрограммы, минимальны (что хорошо, поскольку может быть много узлов вызова для каждой вызываемой подпрограммы). Значения фактических аргументов оцениваются на сайте вызова, поскольку они специфичны для конкретного вызова, и либо помещаются в стек, либо помещаются в регистры, как определено используемым соглашением о вызовах . Фактическая инструкция вызова, такая как «переход и ссылка», затем обычно выполняется для передачи управления коду целевой подпрограммы.
Обработка записи подпрограммы
В вызываемой подпрограмме первый выполненный код обычно называется прологом подпрограммы , так как он выполняет необходимые служебные действия до того, как будет запущен код для операторов подпрограммы.
Для архитектур с набором команд, в которых инструкция, используемая для вызова подпрограммы, помещает адрес возврата в регистр, а не помещает его в стек, пролог обычно сохраняет адрес возврата, помещая значение в стек вызовов, хотя, если вызываемый подпрограмма не вызывает никаких других подпрограмм, она может оставить значение в регистре. Точно так же могут быть переданы значения текущего указателя стека и / или указателя кадра.
Если используются указатели кадра, пролог обычно устанавливает новое значение регистра указателя кадра из указателя стека. Затем можно выделить пространство в стеке для локальных переменных путем постепенного изменения указателя стека.
Язык программирования Forth допускает явную намотку стека вызовов (называемого там «стеком возврата»).
Обработка возврата
Когда подпрограмма готова к возврату, она выполняет эпилог, отменяющий шаги пролога. Обычно это восстанавливает сохраненные значения регистров (например, значение указателя фрейма) из фрейма стека, выталкивает весь фрейм стека из стека, изменяя значение указателя стека, и, наконец, выполняет переход к инструкции по адресу возврата. Согласно многим соглашениям о вызовах элементы, извлекаемые из стека эпилогом, включают в себя исходные значения аргументов, и в этом случае обычно нет дополнительных манипуляций со стеком, которые должны выполняться вызывающей стороной. Однако с некоторыми соглашениями о вызовах ответственность за удаление аргументов из стека после возврата лежит на вызывающей стороне.
Размотка
Возврат из вызванной функции вытолкнет верхний фрейм из стека, возможно, оставив возвращаемое значение. Более общий акт выталкивания одного или нескольких кадров из стека для возобновления выполнения в другом месте программы называется раскручиванием стека и должен выполняться, когда используются нелокальные управляющие структуры, такие как те, которые используются для . В этом случае кадр стека функции содержит одну или несколько записей, определяющих обработчики исключений. Когда генерируется исключение, стек разворачивается до тех пор, пока не будет найден обработчик, готовый обработать (перехватить) тип выброшенного исключения.
В некоторых языках есть другие управляющие структуры, требующие общей раскрутки. Паскаль позволяет глобальному оператору goto передавать управление из вложенной функции в ранее вызванную внешнюю функцию. Эта операция требует, чтобы стек был размотан, удалив столько кадров стека, сколько необходимо для восстановления правильного контекста, чтобы передать управление целевому оператору внутри включающей внешней функции. Аналогично, С имеет и функцию , которые действуют как нелокальный GOTOS. Common Lisp позволяет контролировать то, что происходит при разворачивании стека, с помощью специального оператора.
При применении продолжения стек (логически) разматывается, а затем перематывается вместе со стеком продолжения. Это не единственный способ реализовать продолжения; например, используя несколько явных стеков, приложение продолжения может просто активировать свой стек и намотать значение, которое нужно передать. Язык программирования Scheme позволяет выполнять произвольные переходы в определенных точках при «раскручивании» или «перемотке» стека управления при вызове продолжения.
Структура данных стек
Структура данных – это программный механизм для организации данных таким образом, чтобы их можно было эффективно использовать. Вы уже видели несколько типов структур данных, таких как массивы и структуры. Обе эти структуры данных предоставляют механизмы для хранения данных и эффективного доступа к ним. Существует множество дополнительных, обычно используемых в программировании структур данных, многие из которых реализованы в стандартной библиотеке, и стек является одной из них.
Представьте себе стопку тарелок в кафетерии. Поскольку каждая тарелка тяжелая и они сложены друг на друга, вы можете сделать только одно из трех:
- посмотреть на поверхность верхней тарелки;
- снять верхнюю тарелку со стопки (открывая нижнюю, если она есть);
- поместить новую тарелку на верх стопки (скрывая нижнюю, если она есть).
В компьютерном программировании стек – это структура контейнера данных, который содержит несколько переменных (как массив). Однако в то время как массив позволяет вам получать доступ к элементам и изменять их в любом порядке (так называемый произвольный доступ), стек более ограничен. Операции, которые могут быть выполнены со стеком, соответствуют трем вещам, упомянутым выше:
- посмотреть верхний элемент в стеке (обычно это делается с помощью функции , но иногда называется );
- снять верхний элемент из стека (выполняется с помощью функции );
- поместить новый элемент на верх стека (выполняется с помощью функции ).
Стек – это структура типа «последним пришел – первым ушел» (LIFO, «last-in, first-out»). Последний элемент, помещенный в стек, будет первым извлеченным элементом. Если вы положите новую тарелку поверх стопки, первая тарелка, удаленная из стопки, будет тарелкой, которую вы только что положили последней. Последней положена, первой снята. По мере того, как элементы помещаются в стек, стек становится больше – по мере того, как элементы извлекаются, стек становится меньше.
Например, вот короткая последовательность, показывающая, как работает стек при вставке (push) и извлечении (pop) данных:
Аналогия с тарелками – довольно хорошая аналогия того, как работает стек вызовов, но мы можем провести лучшую аналогию. Представьте себе группу почтовых ящиков, сложенных друг на друга. Каждый почтовый ящик может содержать только один элемент, и все почтовые ящики изначально пустые. Кроме того, каждый почтовый ящик прибивается к почтовому ящику под ним, поэтому количество почтовых ящиков не может быть изменено. Если мы не можем изменить количество почтовых ящиков, как мы можем добиться поведения, подобного стеку?
Во-первых, мы используем маркер (например, наклейку), чтобы отслеживать, где находится самый нижний пустой почтовый ящик. Вначале это будет самый нижний почтовый ящик (внизу стопки). Когда мы помещаем элемент в наш стек почтовых ящиков, мы помещаем его в отмеченный почтовый ящик (который является первым пустым почтовым ящиком) и перемещаем маркер на один ящик вверх. Когда мы извлекаем элемент из стека, мы перемещаем маркер на один почтовый ящик вниз так, чтобы он указывал на верхний непустой почтовый ящик, и удаляем элемент из этого почтового ящика. Всё, что ниже маркера, считается «в стеке». Всё, что находится на уровне маркера или над ним, – не в стеке.