Стек - это блок памяти, который растет. В памяти есть точка, обозначенная регистром rsp
/ esp
, который является вершиной стека. Вся память над ней занята вещами, помещенными в стек, и вся память под ней свободна.
Если вы хотите поместить что-то в стек, вам нужно уменьшить регистр rsp
(это то, что делает инструкция sub
) на количество байтов, которое вам нужно, и rsp
теперь будет указывать на новую область резервирования, которая вам нужна.
Давайте рассмотрим этот простой пример:
rsp
указывает на адрес 100. Каксказал - вся память выше адреса 100 используется, а память ниже 100 свободна. Поэтому, если вам нужно 4 байта, вы уменьшаете rsp
на 4, поэтому он указывает на 96. Поскольку вы только что уменьшили rsp
, вы знаете, что ячейки памяти 96, 97, 98 и 99 являются вашими, и вы можете использовать их. Когда вам нужно больше байтов в стеке, вы снова можете уменьшить rsp
, чтобы получить больше.
Есть два способа размещения вещей в стеке. 1. Вы можете уменьшить rsp
, как показано выше. 2. вы можете использовать инструкцию push
, которая делает то же самое, но за один шаг: push rax
уменьшит rsp
на 8 байт (размер регистра rax
) и сохранит его значение в зарезервированной области.
Иногда также регистр rbp
используется для работы со стеком. Если вам нужна большая область в стеке, например, для локальных переменных, вы резервируете требуемую сумму в стеке, а затем сохраняете текущее значение rsp
в rbp
. Так что rbp
- это своего рода закладка, помнящая, где находится ваш район. Затем вы можете push
больше вещей в стеке, не теряя информацию о том, где была выделенная область.
Перед выходом из функции все вещи, помещенные в стек, должны быть извлечены из него. Это делается с помощью инструкции pop
, противоположной push
- берет значение из стека и перемещает его в регистр, а затем увеличивает rsp
. Или вы можете просто увеличить rsp
, если вам не нужно восстанавливать значения регистров.