Чтение онлайн

на главную - закладки

Жанры

Системное программное обеспечение. Лабораторный практикум

Молчанов Алексей Юрьевич

Шрифт:

При выполнении семантического анализа, генерации кода и оптимизации результирующей программы компилятор должен оперировать характеристиками основных элементов исходной программы – переменных, констант, функций и других лексических единиц входного языка. Эти характеристики могут быть получены компилятором на этапе синтаксического анализа входной программы (чаще всего при анализе структуры блоков описаний переменных и констант), а также дополнены на этапе подготовки к генерации кода (например при распределении памяти).

Набор характеристик, соответствующий каждому элементу исходной программы, зависит от типа этого элемента, от его смысла (семантики) и, соответственно, от той роли, которую он исполняет в исходной и результирующей

программах. В каждом конкретном случае этот набор характеристик может быть свой в зависимости от синтаксиса и семантики входного языка, от архитектуры целевой вычислительной системы и от структуры компилятора. Но есть типовые характеристики, которые чаще всего присущи тем или иным элементам исходной программы. Например для переменной – это ее тип и адрес ячейки памяти, для константы – ее значение, для функции – количество и типы формальных аргументов, тип возвращаемого результата, адрес вызова кода функции. Более подробную информацию о характеристиках элементов исходной программы, их анализе и использовании можно найти в [1, 3, 7].

Главной характеристикой любого элемента исходной программы является его имя. Именно с именами переменных, констант, функций и других элементов входного языка оперирует разработчик программы – поэтому и компилятор должен уметь анализировать эти элементы по их именам.

 

Имя каждого элемента должно быть уникальным. Многие современные языки программирования допускают совпадения (неуникальность) имен переменных и функций в зависимости от их области видимости и других условий исходной программы. В этом случае уникальность имен должен обеспечивать сам компилятор – о том, как решается эта проблема, можно узнать в [1–3, 7], здесь же будем считать, что имена элементов исходной программы всегда являются уникальными.

Таким образом, задача компилятора заключается в том, чтобы хранить некоторую информацию, связанную с каждым элементом исходной программы, и иметь доступ к этой информации по имени элемента. Для решения этой задачи компилятор организует специальные хранилища данных, называемые таблицами идентификаторов, или таблицами символов. Таблица идентификаторов состоит из набора полей данных (записей), каждое из которых может соответствовать одному элементу исходной программы. Запись содержит всю необходимую компилятору информацию о данном элементе и может пополняться по мере работы компилятора. Количество записей зависит от способа организации таблицы идентификаторов, но в любом случае их не может быть меньше, чем элементов в исходной программе. В принципе, компилятор может работать не с одной, а с несколькими таблицами идентификаторов – их количество и структура зависят от реализации компилятора [1, 2].

Принципы организации таблиц идентификаторов

Компилятор пополняет записи в таблице идентификаторов по мере анализа исходной программы и обнаружения в ней новых элементов, требующих размещения в таблице. Поиск информации в таблице выполняется всякий раз, когда компилятору необходимы сведения о том или ином элементе программы. Причем следует заметить, что поиск элемента в таблице будет выполняться компилятором существенно чаще, чем помещение в нее новых элементов. Так происходит потому, что описания новых элементов в исходной программе, как правило, встречаются гораздо реже, чем эти элементы используются. Кроме того, каждому добавлению элемента в таблицу идентификаторов в любом случае будет предшествовать операция поиска – чтобы убедиться, что такого элемента в таблице нет.

На каждую операцию поиска элемента в таблице компилятор будет затрачивать время, и поскольку количество элементов в исходной программе велико (от единиц до сотен тысяч в зависимости от объема программы), это время будет существенно влиять на общее время компиляции. Поэтому таблицы идентификаторов должны быть организованы таким образом, чтобы компилятор имел возможность максимально быстро выполнять

поиск нужной ему записи таблицы по имени элемента, с которым связана эта запись.

Можно выделить следующие способы организации таблиц идентификаторов:

• простые и упорядоченные списки;

• бинарное дерево;

• хэш-адресация с рехэшированием;

• хэш-адресация по методу цепочек;

• комбинация хэш-адресации со списком или бинарным деревом.

Далее будет дано краткое описание всех вышеперечисленных способов организации таблиц идентификаторов. Более подробную информацию можно найти в [3, 7].

Простейшие методы построения таблиц идентификаторов

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

Поиск нужного элемента в таблице будет в этом случае выполняться путем последовательного перебора всех элементов и сравнения их имени с именем искомого элемента, пока не будет найден элемент с таким же именем. Тогда если за единицу времени принять время, затрачиваемое компилятором на сравнение двух строк (в современных вычислительных системах такое сравнение чаще всего выполняется одной командой), то для таблицы, содержащей N элементов, в среднем будет выполнено N/2 сравнений.

Время, требуемое на добавление нового элемента в таблицу (Tд), не зависит от числа элементов в таблице (N). Но если N велико, то поиск потребует значительных затрат времени. Время поиска (Tп) в такой таблице можно оценить как Tп = O(N). Поскольку именно поиск в таблице идентификаторов является наиболее часто выполняемой компилятором операцией, такой способ организации таблиц идентификаторов является неэффективным. Он применим только для самых простых компиляторов, работающих с небольшими программами.

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

Алгоритм логарифмического поиска заключается в следующем: искомый символ сравнивается с элементом (N + 1)/2 в середине таблицы; если этот элемент не является искомым, то мы должны просмотреть только блок элементов, пронумерованных от 1 до (N + 1)/2 – 1, или блок элементов от (N + 1)/2 + 1 до N в зависимости от того, меньше или больше искомый элемент того, с которым его сравнили. Затем процесс повторяется над нужным блоком в два раза меньшего размера. Так продолжается до тех пор, пока либо искомый элемент не будет найден, либо алгоритм не дойдет до очередного блока, содержащего один или два элемента (с которыми можно выполнить прямое сравнение искомого элемента).

Так как на каждом шаге число элементов, которые могут содержать искомый элемент, сокращается в два раза, максимальное число сравнений равно 1 + log2 N. Тогда время поиска элемента в таблице идентификаторов можно оценить как Tп = O(log2 N). Для сравнения: при N = 128 бинарный поиск требует самое большее 8 сравнений, а поиск в неупорядоченной таблице – в среднем 64 сравнения. Метод называют «бинарным поиском», поскольку на каждом шаге объем рассматриваемой информации сокращается в два раза, а «логарифмическим» – поскольку время, затрачиваемое на поиск нужного элемента в массиве, имеет логарифмическую зависимость от общего количества элементов в нем.

Поделиться:
Популярные книги

Мастер Разума III

Кронос Александр
3. Мастер Разума
Фантастика:
героическая фантастика
попаданцы
аниме
5.25
рейтинг книги
Мастер Разума III

Часовое имя

Щерба Наталья Васильевна
4. Часодеи
Детские:
детская фантастика
9.56
рейтинг книги
Часовое имя

Печать мастера

Лисина Александра
6. Гибрид
Фантастика:
попаданцы
технофэнтези
аниме
фэнтези
6.00
рейтинг книги
Печать мастера

Идеальный мир для Лекаря

Сапфир Олег
1. Лекарь
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря

Кротовский, не начинайте

Парсиев Дмитрий
2. РОС: Изнанка Империи
Фантастика:
городское фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Кротовский, не начинайте

Эволюция мага

Лисина Александра
2. Гибрид
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Эволюция мага

Прорвемся, опера! Книга 3

Киров Никита
3. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 3

Демон

Парсиев Дмитрий
2. История одного эволюционера
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Демон

Прорвемся, опера! Книга 2

Киров Никита
2. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 2

#Бояръ-Аниме. Газлайтер. Том 11

Володин Григорий Григорьевич
11. История Телепата
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
#Бояръ-Аниме. Газлайтер. Том 11

Офицер

Земляной Андрей Борисович
1. Офицер
Фантастика:
боевая фантастика
7.21
рейтинг книги
Офицер

Призыватель нулевого ранга. Том 3

Дубов Дмитрий
3. Эпоха Гардара
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Призыватель нулевого ранга. Том 3

Сделай это со мной снова

Рам Янка
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Сделай это со мной снова

Злыднев Мир. Дилогия

Чекрыгин Егор
Злыднев мир
Фантастика:
фэнтези
7.67
рейтинг книги
Злыднев Мир. Дилогия