- •Глава 14 Генерация
- •1 Примером контекстной зависимости, часто встречающейся во многих языках программирования, может служить необходимость предварительно описать идентификатор до его первого использования.
- •Этапы семантического анализа
- •Идентификация лексических единиц языков программирования
- •Распределение памяти. Принципы распределения памяти
- •1 Некоторые вычислительные системы даже вообще не могут работать с адресами, не кратными определенному числу байт.
- •Глобальная и локальная память
- •Статическая и динамическая память
- •Дисплей памяти процедуры (функции). Стековая организация дисплея памяти
- •Стековая организация дисплея памяти процедуры (функции)
- •Память для типов данных (rtti-информация)
- •Способы внутреннего представления программ
- •Синтаксические деревья
- •Многоадресный код с явно именуемым результатом (тетрады)
- •Многоадресный код с неявно именуемым результатом (триады)
- •Обратная польская запись операций
- •Вычисление выражений с помощью обратной польской записи
- •Схемы су-перевода
- •Пример схемы су-перевода дерева операций на язык ассемблера
- •Пример схемы су-перевода дерева операций в последовательность триад
- •Оптимизация линейных участков программы Принципы оптимизации линейных участков
- •Свертка объектного кода
- •Исключение лишних операций
- •Другие методы оптимизации программ Оптимизация вычисления логических выражений
- •Оптимизация передачи параметров в процедуры и функции
- •Оптимизация циклов
- •1 Незначительный выигрыш в скорости достигается за счет исключения всех операций с индуктивной переменной, однако объем программы может существенно возрасти.
- •Машинно-зависимые методы оптимизации
- •Распределение регистров процессора
- •Оптимизация кода для процессоров, допускающих распараллеливание вычислений
Статическая и динамическая память
Статическая область памяти — это область памяти, размер которой известен на этапе компиляции. Поскольку для статической области памяти известен ее размер, компилятор всегда может выделить эту область памяти и связать ее с соответствующим элементом программы. Поэтому для статической области памяти компилятор порождает некоторый адрес (как правило, это относительный адрес в программе — см. раздел «Принципы функционирования систем программирования», глава 15). B статическую область памяти попадает большинство переменных и констант исходного языка.
Статические области памяти обрабатываются компилятором самым простейшим образом, поскольку напрямую связаны со своим адресом.
Динамическая область памяти — это область памяти, размер которой на этапе компиляции программы не известен. Размер динамической области памяти будет известен только в процессе выполнения результирующей программы. Поэтому для динамической области памяти компилятор не может непосредственно выделить адрес — для нее он порождает фрагмент кода, который отвечает за распределение памяти (ее выделение и освобождение). Как правило, с динамическими областями памяти связаны многие операции с указателями и с экземплярами объектов (классов) в объектно-ориентированных языках программирования.
Динамические области памяти, в свою очередь, можно разделить на динамические области памяти, выделяемые пользователем, и динамические области памяти, выделяемые непосредственно компилятором.
Динамические области памяти, выделяемые пользователем, появляются в тех случаях, когда разработчик исходной программы явно использует в тексте про-
Рассуждения о возможности или невозможности использовать адреса тех или иных элементов программы в качестве результата функции в приведенном примере носят чисто теоретический характер. Вопрос о практическом использовании такого рода функций автор оставляет открытым, поскольку он касается проблем читаемости и наглядности исходного текста программ, а также стиля программирования. Эти проблемы представляют большой интерес, но выходят за рамки данного учебного пособия.
граммы функции, связанные с распределением памяти (примером таких функций являются New и Dispose в языке Pascal, malloc и free в языке C, new и delete в языке C++ и др.). Функции распределения памяти могут использовать либо напрямую средства OC, либо средства исходного языка (которые, в конце концов, все равно основаны на средствах OC). B этом случае за своевременное выделение и освобождение памяти отвечает сам разработчик, а не компилятор (принципы динамического распределения памяти в OC более подробно описаны в части 1 этого пособия). Компилятор должен только построить код вызова соответствующих функций и сохранения результата — в принципе, для него работа с динамической памятью пользователя ничем не отличается от работы с любыми другими функциями и дополнительных сложностей не вызывает.
Другое дело — динамические области памяти, выделяемые компилятором. Они появляются тогда, когда пользователь использует типы данных, операции над которыми предполагают перераспределение памяти, не присутствующее в явном виде в тексте исходной программы1. Примерами таких типов данных могут служить строки в некоторых языках программирования, динамические массивы и, конечно, многие операции над экземплярами объектов (классов) в объектно-ориентированных языках (наиболее характерный пример — тип данных string в версиях Borland Delphi языка Object Pascal). B этом случае сам компилятор отвечает за порождение кода, который будет всегда обеспечивать своевременное выделение памяти под элементы программы, и за освобождение ее по мере использования. Многие компиляторы объектно-ориентированных языков программирования используют для этих целей специальный менеджер памяти, к которому обращаются во всех случаях при явном или неявном использовании динамической памяти. Код менеджера памяти включается в текст результирующей программы или поставляется в виде отдельной библиотеки.
Как статические, так и динамические области памяти сами по себе могут быть глобальными или локальными.
