TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • flag Russian
    Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Николай Тузов

14 Oct 2025, 11:26

Открыть в Telegram Поделиться Пожаловаться

👴 Внутреннее устройство памяти — что такое Stack и зачем он нужен?

Что ж, продолжаю традиционно готовить вас к новому большому ролику сильно заранее перед его выходом. Когда он будет? Пока рано говорить, не хочу вас обмануть. Точно не в ближайшее время. О чём? Можете догадаться 🥂

Представим, что мы проектируем язык программирования и нужно решить: где хранить локальные переменные функций?

Допустим, у нас есть функция A() (main(), если хотите). У неё есть свои локальные переменные, которые больше никому не доступны, и их надо где-то разместить.

Мы любим простоту, поэтому начнём с самого простого решения — давайте просто выдадим A() какой-то адрес в памяти, например такой: 0x1000. Далее она сама распределит адреса для своих переменных, начиная с этой точки:

func A() {
x := 1 // 0x1000
y := 2 // 0x1008
}

И это отлично работает! Компилятор видит две переменные, резервирует 16 байт, всё просто.

————

Добавим вторую функцию

Теперь A вызывает B. Какой адрес дать B?

func A() {
x := 1 // 0x1000
y := 2 // 0x1008
B() // какой адрес?
}

func B() {
a := 10 // ???
b := 20 // ???
}

0x1000 не подходит — там уже лежат x и y функции A, которые ей ещё понадобятся после возврата из B. Затрём их — сломаем программу 😩

Самое простое и очевидное решение: выделенный для B адрес должен начинаться после адреса A. A занимает 16 байт: 0x1000 + 15 = 0x100F, за ним идёт 0x1010 (hex представление). Значит B начнётся с 0x1010:

func A() {
x := 1 // 0x1000
y := 2 // 0x1008
B() // начнётся с 0x1010
}

func B() {
a := 10 // 0x1010
b := 20 // 0x1018
}

Если в цепочке вызовов появляются новые функции, действуем по аналогии.

Итого получаем следующую картину:

// Указатель: 0x1000

func A() {
// Начинаем с текущей позиции: 0x1000
x := 1 // 0x1000
y := 2 // 0x1008
// Занимает 16 байт, сдвигаем указатель: 0x1000 → 0x1010
B()
}

func B() {
// Начинаем с текущей позиции: 0x1010
a := 10 // 0x1010
b := 20 // 0x1018
// Занимает 16 байт, сдвигаем: 0x1010 → 0x1020
C()
}

func C() {
// Начинаем с текущей позиции: 0x1020
k := 100 // 0x1020
}

Всё отлично работает, и нам даже не важно сколько функций будет в цепочке — A может даже рекурсивно вызывать саму себя, решение автоматически масштабируется сколь угодно долго.

Вернее, до того самого момента stack overflow 🙃
Теперь вы понимаете что это такое — если цепочка слишком длинная, стек не влезает в выделенную под него память и программа падает со знаменитой ошибкой.

————

А как освобождать память?

Когда C завершается, её память больше не нужна. Хотим вернуть указатель назад. Но на сколько? На размер C? Нет, потому что внутри C могли вызваться другие функции!

Правильный способ: каждая функция запоминает, где она начиналась, и при завершении возвращает указатель туда:

func A() {
saved := 0x1000 // запомнили начало A
// сдвинули: 0x1000 → 0x1010
x := 1
y := 2
B()
// B завершилась, возвращаем: 0x1010 → 0x1000
}

func B() {
saved := 0x1010 // запомнили начало B
// сдвинули: 0x1010 → 0x1020
a := 10
b := 20
C()
// C завершилась, возвращаем: 0x1020 → 0x1010
}

func C() {
saved := 0x1020
// сдвинули: 0x1020 → 0x1028
k := 100
// возвращаем: 0x1028 → 0x1020
}

————

Таким образом указатель растёт при вызовах и возвращается при завершении. Последняя вызванная функция первой освобождает память. Узнаёте? Это же классическая структура LIFO — Last In, First Out!

И теперь нас можно поздравить, ведь мы изобрели тот самый стек — вот так просто, принимая очевидные минималистичные решения. Такая структура получилась естественным образом из логики адресации.

————

Но что если у нас появляется переменная, которая будет доступна в разных функциях? Такое часто случается когда мы работаем с указателями. И это уже другая история.. Обсудим это в отдельном посте.

#guide #memory

11.7k 2 245 10 224
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot