Хороший курс по тому, как работать с памятью на низком уровне. Построение курса такое: мы вроде как знаем язык python, но он нам оказался не удел и поэтому мы пишем преемника Sneklang. Мы хотим сделать язык, где не надо будет следить за памятью, поэтому нам нужен будет реализовать сборщик мусора. Чтобы это сделать надо разобраться с следующим набором вещей:

  1. синтаксис C
  2. доступные операции для построения структур: struct, union, enum
    1. union для меня оказался открытием, потому что это область памяти с одним значением разных типов
      1. при этом перед использованием ты должен как-то понять что там лежит, иначе получишь белиберду
  3. реализовать примитив Stack
  4. послушать как устроена память: а именно стек, и хип
    1. стек - создаётся на вызове функции и представляет собой линейную адресацию памяти в рамках неё
    2. хип/куча - память, которая будет жить после окончания функции и которой требуется ручной контроль “жизни”
  5. разобраться с указателями, массивами и указателями на указатели
    1. указатель - это адрес начала данных в массиве памяти, и здесь массив памяти имеется в виду как классический массив
    2. при работе со структурами обращение -> - это синтаксис на зарезолвить значение и обратиться по полю
    3. array decay - при передаче указателя на массив в функцию, sizeof будет возвращать размер указателя, а не массива
  6. собрать на основе всех пунктов структуры, которые смогут одновременно держать строки, числа и динамические массивы
    1. здесь мы узнаём, что typedef нам нужен для будущих объявлений структур при работе с рекурсивными типами
  7. написать для них сборщик через подсчёт ссылок
    1. добавляем в структуру числовой счётчик
    2. при указании и использование объекта (например, добавление в массив) счётчик увеличиваем
    3. при удаление объекта уменьшаем счётчик, если тут ещё и ноль освобождаем память
    4. не работает с циклическими зависимостями
  8. написать для них сборщик через mark-and-sweep - это просто!
    1. mark - пометить всё как неиспользуемое
    2. trace - от корня стека пометить всё используемое как true
    3. sweep - пройтись по всем объектам, если неиспользуется, то освободить память
    4. этот сборщик может отработать циклические зависимости (похоже на disjoint union), НО может приводить к stop the world операции

Важное про garbage collector здесь, что это всегда порожняя работа. То есть, из-за того что поленились писать alloc/free в рамках программ, мы перекладываем это на автоматический алгоритм, которому требуется процессорное время. Оно хорошо работает на обнаруженных эвристиках (большинство объектов живут только в рамках своей же функции), но всё равно может приводить к неравномерному времени обработки данных.

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

Пример понятных картинок из курса про указатели на указатели: Pasted image 20251117105947.png