Стек: push и pop
Урок знакомит со стеком — временным складом данных, на котором держатся вызовы функций.
Стек — область памяти, работающая по принципу «последним пришёл — первым вышел» (LIFO), с вершиной в регистре
rsp.
Зачем нужен стек
Регистров всего 16, а данных в программе много. Стек — это удобное временное хранилище: можно положить значение «наверх» командой push и забрать его позже командой pop. На стеке держатся локальные переменные, сохранённые регистры и адреса возврата из функций.
push и pop
push rax уменьшает rsp на 8 и записывает rax по новому адресу. pop rbx читает значение с вершины и увеличивает rsp на 8:
push rax ; rsp -= 8; [rsp] = rax
push rbx ; положили ещё одно значение сверху
pop rcx ; rcx = [rsp]; rsp += 8 (забрали rbx)
pop rdx ; rdx = [rsp]; rsp += 8 (забрали rax)Порядок важен: что положили последним, то и заберём первым. Поэтому здесь rcx получит бывшее rbx, а rdx — бывшее rax.
Стек растёт вниз
Любопытная деталь: при push адрес уменьшается. Стек растёт от старших адресов к младшим — навстречу остальной памяти. Схема:
высокие адреса
| ... старые данные ...
| [значение 1] <- положили первым
| [значение 2] <- rsp (вершина) после второго push
v
низкие адресаКак работает под капотом
Смоделируем стек обычным списком Python, где append/pop ведут себя как push/pop:
stack = []
stack.append("rax=10") # push
stack.append("rbx=20") # push
print("вершина:", stack[-1])
first = stack.pop() # pop -> забираем последнее
second = stack.pop() # pop -> забираем предыдущее
print("забрали:", first)
print("потом :", second)Вывод:
вершина: rbx=20 забрали: rbx=20 потом : rax=10
Видно правило LIFO: последним положили rbx, его же первым и достали. Эта дисциплина — основа того, как функции аккуратно сохраняют и восстанавливают данные.
Частые ошибки
- Несбалансированные push/pop. Сколько положили, столько и заберите, иначе
rspсъедет иretуведёт не туда. - Думать, что стек растёт вверх. На x86
pushуменьшает адрес — стек растёт вниз. - Хранить адрес локальных данных после возврата. После
pop/возврата это место уже не ваше.
Итог
- Стек — временное LIFO-хранилище, вершина в
rsp. pushуменьшаетrspи пишет значение,popчитает и увеличиваетrsp.- Стек растёт вниз:
pushуменьшает адрес. - push и pop должны быть сбалансированы, иначе
rspсобьётся.