
- •Компиляция. Основные понятия. Контекст компилятора
- •Этапы, фазы и проходы
- •Фаза анализа. Основные понятия.
- •Фаза синтеза. Основные понятия.
- •Методы определения языка
- •Понятие регулярных выражений
- •Понятие грамматики
- •8.Иерархия грамматик по Хомскому
- •9 . Порождения синтаксические деревья
- •Неоднозначность
- •11.Понятие лексического анализа
- •12.Лексический анализ с помощью регулярных выражений
- •Лексический анализ с помощью конечных автоматов
- •14. Лексический анализ с помощью Lex
- •15. Нисходящий анализ. Критерии принятия решений.
- •16. Понятие ll(1)-грамматики
- •17. Рекурсивный спуск. Расширенная форма записи правил для исключения рекурсивных вызовов.
- •18. Рекурсивный спуск. Комбинирование рекурсии и итерации.
- •19. Преобразование грамматик. Удаление левой рекурсии
- •20. Преобразование грамматик. Факторизация
- •21. Введение действий в грамматику
- •22. Восходящий синтаксический анализ. Основные понятия. Критерий принятия решений
- •Восходящий синтаксический анализ. Таблица синтаксического анализа.
- •25. Характеристический конечный автомат.
- •Восходящий синтаксический анализ. Slr(1), lalr(1), общий алгоритм формирования таблицы lr(1)-анализа.
- •Восходящий синтаксический анализ. Генератор восходящих анализаторов yacc. Основные понятия.
- •Семантический анализ. Не-контекстно-свободные характеристики языков.
- •Семантический анализ. Таблица символов.
- •Семантический анализ. Таблица типов.
- •31.Семантический анализ. Таблицы функций. Таблицы меток
- •32. Распределение памяти. Классификация памяти
- •33. Распределение памяти. Стек времени выполнения. Определение области видимости.
- •34. Распределение памяти. Стековый фрейм.
- •35. Распределение памяти. Дисплей.
- •35. Адреса времени компиляции. Простые адреса. Адресация элементов статического массива.
- •37. Распределение памяти. Адреса времени компиляции. Адреса динамического массива
- •38. Куча. Основные понятия. Методы автоматического освобождения памяти.
33. Распределение памяти. Стек времени выполнения. Определение области видимости.
Распределение глобальной памяти осуществляется достаточно просто: область пространства (куча (heap)) увеличивается настолько, насколько это необходимо. Освобождение этой области памяти осуществляется намного сложнее, поскольку данный процесс трудно связать с процессом распределения памяти. Существует два основных вопроса, связанных с распределением и освобождением глобальной памяти.
Доступность памяти для освобождения определяется во время выполнения программы, что неизбежно приводит к некоторого рода служебным издержкам при выполнении программы.
После освобождения некоторого участка памяти в куче возникают чистые участки, которые обычно требуют сжатия для более эффективного использования памяти.
Отметим, что стек и куча могут удобно сосуществовать вместе, если их увеличение происходит по направлению друг к другу. В этом случае область статической памяти может размещаться на одном или другом конце пространства памяти, как это изображено на рисунке. Вмешательство извне потребуется только в том случае, когда взаимное расширение стека и кучи приведет к их “встрече”, т.е. нехватке памяти. Обычно в подобном случае определяется недоступное пространство кучи и происходит ее сжатие.
При рассмотрении адресов переменных и т.д. следует отметить, что иногда (например, при использовании статической памяти) адреса времени выполнения известны во время компиляции. В то же время чаще имеем обратную ситуацию, когда адреса времени выполнения должны вычисляться, исходя из множества факторов, часть которых известна в процессе компиляции, а часть неизвестна до начала выполнения программы. В этих случаях аспекты адреса, известные при компиляции, называются адресом времени компиляции.
В языке С для переменных имеется четыре возможных класса памяти: static, auto, extern и register. Для статических переменных память выделяется на все время программы. Для переменных класса auto (класс по умолчанию) память выделена до момента завершения работы составного оператора, в котором были объявлены данные переменные. Таким образом, более удобной для данных переменных является память в стеке. Для переменных класса extern память выделяется в другом файле. Значения переменных класса register хранятся в регистре, если компилятор способен организовать это удобным образом, в противном случае такие переменные эквивалентны переменным auto.
Помимо памяти, необходимой переменным программы на С, с помощью malloc можно выделить память для значений, к которым обращаются посредством указателей, например:
p = malloc(sizeof(int));
В данном выражении выделяется достаточно памяти для целого значения, а р является указателем на это значение. Эта память может освобождаться после того, как ни одна переменная программы (в том числе р) не будет указывать на данную область памяти. В то же время, поскольку это невозможно определить в процессе компиляции, область, выделяемая посредством malloc, обязательно должна располагаться в куче.
Ранние языки программирования, имели статическую память, размер которой был известен во время компиляции. Выделенный объем памяти уже не освобождался, поэтому применялась очень простая модель распределения памяти – необходимая память выделялась от одного края доступного пространства по направлению к другому. Более современные языки, обычно имеют блочную структуру, что позволяет переменным, объявленным в различных блоках, совместно использовать одну область памяти. Удобными являются основанные на стеках модели распределения памяти, которые позволяют повторно использовать ранее выделенную память.
Используемый в этих моделях стек времени выполнения подобен рассмотренной таблице символов, но с одним важным отличием: стек времени выполнения – это структура времени выполнения программы, а не времени компиляции. В то же время, многие операции над таблицей символов во время компиляции являются копиями операций над стеком времени выполнения.