
- •Министерство образования и науки Российской Федерации
- •Цель лекции
- •План лекции
- •1 Происхождение вычислительных машин
- •2 Изучение алгоритмов
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Биты и их хранение
- •2 Оперативная память
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Устройства внешней памяти
- •3 Хранение и поиск файлов
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Представление числовых значений
- •2 Хранение целых чисел
- •3 Хранение дробей
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Представление текста
- •2 Представление изображений
- •Достоинства пиксельной графики
- •Недостатки пиксельной графики
- •Достоинства векторной графики
- •Недостатки векторной графики
- •3 Представление звука
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Сжатие изображений
- •2 Ошибки передачи данных
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Архитектура эвм
- •2 Связь процессора с другими устройствами
- •3 Другие архитектуры
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Машинный язык
- •2 Пример машинного языка
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Выполнение программы
- •2 Пример выполнения программы
- •3 Программы и данные
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Логические операции
- •2 Операции сдвига
- •3 Арифметические операции
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Эволюция операционных систем
- •2 Архитектура операционных систем
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Понятие процесса
- •2 Управление процессами
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Классификация сетей
- •2 Сетевые протоколы*
- •3 Безопасность сетей
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Понятие алгоритма
- •2 Представление алгоритма
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Теория решения задач
- •2 Общие методы решения задач
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Алгоритм последовательного поиска
- •2 Управление циклами
- •3 Алгоритм сортировки методом вставки
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Алгоритмы поиска и сортировки
- •2 Управление рекурсией
- •3 Разработка рекурсивных процедур
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Эффективность алгоритмов
- •2 Верификация программ
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Эволюция и классификация языков программирования
- •2 Концепции традиционного программирования
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Процедурные единицы
- •2 Реализация языка программирования
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Объектно-ориентированное программирование
- •2 Декларативное программирование
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Структуры данных
- •Integer Scores (2.9).
- •2 Статические и динамические структуры
- •3 Указатели
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Массивы
- •2 Списки
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Структура и функции стека
- •2 Реализация стека
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Очереди
- •2 Деревья
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Пользовательские типы данных
- •Int Age;
- •2 Классы
- •Int *StackEntries;
- •3 Стандартная библиотека шаблонов
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Роль операционной системы
- •2 Последовательные файлы
- •3 Вопросы программирования
- •0000000010000110
- •001100010011001100110100
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Основные положения индексации
- •2 Вопросы программирования
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Основные положения хеширования
- •2 Вопросы программирования
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Общие вопросы
- •2 Система управления базой данных
- •3 Поддержка целостности баз данных
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Модели баз данных
- •2 Реляционная модель баз данных
- •3 Объектно-ориентированные базы данных
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Интеллект и машины
- •2 Распознавание образов
- •3 Мышление
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Искусственные нейронные сети
- •2 Генетические алгоритмы
- •Контрольные вопросы
- •Невычислимые функции Цель лекции
- •План лекции
- •1 Основы машины Тьюринга
- •2 Невычислимая функция
- •3 Сложность задач
- •Листинг 1. Процедура MergeLists для объединения двух упорядоченных списков
- •Листинг 2. Алгоритм сортировки слиянием, реализованный в процедуре MergeSort
- •Контрольные вопросы
- •Цель лекции
- •План лекции
- •1 Шифрование с открытым ключом
- •2 Модульная арифметика
- •Контрольные вопросы
- •Литература
- •Internet-ресурсы
2 Реализация языка программирования
В этом вопросе мы рассмотрим процесс преобразования программы, написанной на языке программирования высокого уровня, в набор машинных команд.
Процесс трансляции программы. Процесс преобразования программы с одного языка на другой называется трансляцией программы (translation). Программа в первоначальной форме называется исходной программой (source program); преобразованная программа называется объектной (object program). Процесс трансляции включает в себя три стадии: лексический анализ, синтаксический анализ и генерация объектного кода. Эти стадии выполняются элементами транслирующей программы, которые называются лексическим анализатором (lexical analyzer), синтаксическим анализатором (parser) и генератор объектного кода (code generator) (рис. 7).
Рисунок 6 – Процесс трансляции программы
В процессе лексического анализа распознаются цепочки символов, которые представляют собой отдельные элементы, Например, три символа 153 интерпретируются программой не как три отдельных числа, а как одно. Точно так же слово, которое встречается в программе, хотя и состоит из отдельных букв, распознается как целый элемент. Для большинства людей лексический анализ не представляет особой трудности. Когда нас просят прочитать что-либо вслух, мы произносим слова, а не отдельные символы.
Таким образом, лексический анализатор символ за символом считывает исходную программу, определяет, какие группы символов представляют собой отдельные элементы, и классифицирует эти элементы согласно тому, являются ли они числовыми значениями, словами, арифметическими операторами и т. д. По мере классификации элементов лексический анализатор порождает цепочки битов, которые называются лексемами (tokens), и передает их синтаксическому анализатору. При этом лексический анализатор пропускает все комментарии.
Синтаксический анализатор рассматривает программу как совокупность лексических единиц (лексем), а не отдельных символов. Его задача состоит в том, чтобы сгруппировать эти элементы в отдельные высказывания. Синтаксический анализ представляет собой процесс определения грамматической структуры программы и роли каждого ее компонента. Эта техническая сторона анализа может вызвать задержку при чтении следующего предложения:
Человек, которого лошадь, проигравшая скачки, сбросила, не был ранен.
Для упрощения процесса синтаксического анализа в ранних языках программирования каждый оператор программы располагался в определенном месте листа. Такие языки называются языками с фиксированным форматом (fixed-format languages). Сейчас большинство языков программирования являются языками с произвольным форматом, то есть в них положение оператора не является существенным. Преимущество таких языков программирования состоит в том, что программист имеет возможность располагать записанную программу так, чтобы се было легче читать человеку. В этом случае принято использовать отступы, чтобы читателю было проще понять структуру оператора. Вместо того чтобы записать
if Cost < CashOnHand then заплатить наличными else использовать кредитную карту программист может записать
if Cost < CashOnHand
then заплатить наличными
else использовать кредитную карту
Для того чтобы машина могла обработать программу, написанную на языке с произвольным форматированием, синтаксис языка должен быть таким, что определение структуры программы не зависит от количества пробелов, используемых в исходной программе. С этой Цель лекциию в большинстве языков программирования для обозначения конца высказывания используются пунктуационные знаки, например точка с запятой, и ключевые слова (key words), такие как if, then и else, для обозначения начала отдельных конструкций. Такие ключевые слойа часто называют зарезервированными словами (reserved words), поскольку программист не может использовать их в программе для каких-либо других целей.
Реализация JAVA. В случае веб-страниц с анимацией программное обеспечение, которое управляет анимацией, передается по Интернету вместе со страницей. Если это программное обеспечение поставляется в форме исходной программы, то при просмотре страницы возникнут трудности, поскольку это программное обеспечение нужно преобразовать в машинный язык. Однако если записывать эти программы на машинном языке, то придется создавать разные версии страницы в зависимости от машинного языка, используемого в машине, пытающейся получить доступ к странице.
Компания Sun Microsystems решила эту проблему, создав универсальный «машинный язык», который называется байт-кодом. В этот код преобразуются все исходные программы, написанные на языке Java. Хотя байт-код — не совсем машинный язык, его может быстро выполнить любая машина с помощью соответствующей интерпретирующей программы. Сегодня такие интерпретирующие программы становятся частью программ браузера. Таким образом, если программное обеспечение, управляющее веб-страницей, написано на Java и преобразовано в байт-код, то этот код можно передать браузеру для более эффективного отображения анимации.
В основе процесса синтаксического анализа лежит набор синтаксических правил, которые определяют синтаксис языка программирования. Одним из способов представления этих правил является синтаксическая диаграмма (syntax diagram), то есть графическое изображение грамматической структуры программы. Синтаксическая диаграмма оператора if-then-else представлена на рис. 8. Она показывает, что структура if-then-else начинается со слова if, за ним следует Логическое выражение, слово then и Высказывание. После этой комбинации может находиться слово else и Высказывание. Обратите внимание на то, что элементы, которые действительно присутствуют в операторе if-then-else, заключены в овалы, а элементы, которые требуют дальнейшего описания, такие как Логическое выражение и Высказывание, заключены в прямоугольники. Элементы, требующие дальнейшего описания (в прямоугольниках), называются нетерминальными (nonterminals); элементы в овалах, называются терминальными (terminals).
В полном описании синтаксиса языка нетерминальные элементы определяются с помощью дополнительных диаграмм.
Рисунок 7 – Синтаксическая диаграмма оператора if-then-else
В качестве более сложного примера приведем несколько синтаксических диаграмм, описывающих синтаксис структуры Выражение, которая является простым арифметическим выражением (рис. 8). На первой диаграмме Выражение состоит из Элемента, за которым может следовать символ + или - и другое Выражение. Вторая диаграмма описывает Элемент, состоящий либо из одного Множителя, либо из Множителя, символов х или -5- и другого Элемента. И наконец, последняя диаграмма описывает Множитель, который является одним из символов х, у или z.
Рисунок 8 – Синтаксические диаграммы, описывающие простое алгебраическое выражение
Отдельное арифметическое выражение можно представить с помощью дерева синтаксического анализа (parse tree). Дерево синтаксического анализа (рис. 10) для цепочки
х + у х z
строится на базе синтаксических диаграмм (см. рис. 9). Обратите внимание на то, что в основании дерева находится нетерминальный элемент Выражение, и на каждом последующем уровне нетерминальные элементы разбиваются на составные части до тех пор, пока не будут получены символы цепочки. В частности, дерево
Рисунок 9 – Дерево синтаксического анализа для цепочки х + у х z
показывает, что цепочка х + у х z состоит из Элемента, который является Множителем х, символа + и Выражения, которое является Элементом у х z.
Процесс синтаксического анализа программы, в сущности, представляет собой построение дерева синтаксического анализа для исходного кода. Синтаксическое дерево отражает то, как синтаксический анализатор понимает грамматическое строение программы. Поэтому синтаксические правила, описывающие грамматическую структуру программы, должны исключать возможность построения двух синтаксических деревьев для одной цепочки, поскольку это приведет к неоднозначности. Ошибки такого рода трудно заметить. Например, диаграмма на рис. 8 содержит означенный дефект. Она позволяет построить два синтаксических дерева (рис. 10) для одного высказывания
if Bl then if В2 then SI else S2
Обратите внимание на то, что эти деревья принципиально отличаются. Первое дерево предполагает, что выражение S2 выполняется, если условие В1 ложно; второе означает, что S2 выполняется, только если условие В1 истинно, а условие В2 ложно.
Синтаксические определения формальных языков программирования создаются так, чтобы избежать неоднозначности. В нашем псевдокоде для этого мы использовали скобки. Например, для того чтобы различать два возможных способа интерпретации приведенного выше выражения, можно записать:
if Bl
then (if B2 then SI)
else S2
и
if Bl
then (if B2 then SI else S2)
Когда синтаксический анализатор обрабатывает операторы описания, он записывает содержащуюся в них информацию в таблицу, которая называется таблицей имен (symbol table). Таблица имен содержит информацию о том, какие переменные объявлены в программе и какие типы данных или структур данных поставлены им в соответствие. С помощью этой информации синтаксический анализатор обрабатывает исполняемые операторы, такие как
Z <- X + Y;
Рисунок 10 – Два синтаксических дерева высказывания if Bl then if В2 then SI else S2
Для того чтобы определить значение символа +, анализатору необходимо знать тип данных переменных X и Y. Если X — вещественная переменная, a Y — символ, тогда их сложение просто бессмысленно, и следует сообщить об ошибке. Если X и Y — целочисленные переменные, тогда анализатор предпишет генератору кода создать команду машинного языка с кодом операции сложения двух целых чисел. Если оба слагаемых являются переменными вещественного типа, то генератор кода будет использовать код операции для сложения с плавающей точкой.
Приведенный выше оператор может включать в себя переменные разных типов. Например, если X — целочисленная переменная, a Y — вещественная, то операцию сложения все-таки можно выполнить. В таком случае генератор кода создает команду преобразования типа одного из значений, а затем выполняет сложение. Такое преобразование типа значения называется приведением типов (coercion).
Многие разработчики языков неодобрительно относятся к приведению типов. Они утверждают, что необходимость приведения типов свидетельствует о наличии недостатков в строении программы, и, следовательно, анализатор не должен предоставлять такую операцию. В результате большинство современных языков программирования являются языками со строгим контролем типов (strongly typed), то есть все операции, выполняемые программой, должны включать данные совместимых типов. Синтаксические анализаторы этих языков при возникновении конфликта типов всегда сообщают об ошибке.
Последняя стадия процесса трансляции программы — это генерация кода, то есть создание команд машинного языка, выполняющих выражения, распознанные синтаксическим анализатором. Во время генерации кода возникает множество трудностей, и одна из них — это создание эффективного кода. Рассмотрим, например, трансляцию последовательности двух операторов
х у + z;
w х + z;
Можно рассматривать эти операторы независимо друг от друга. Однако в этом случае мы не получим эффективного кода. Генератор кода должен распознать, что после выполнения первого оператора значения х и z уже будут находиться в регистрах центрального процессора, и, следовательно, их не нужно повторно загружать из памяти перед выполнением второго оператора. Этот процесс называется оптимизацией программы (code optimization) и является важной задачей генератора кода.
Лексический и синтаксический анализ и генерация кода не выполняются строго друг за другом. Вместо этого все эти процессы протекают одновременно. Лексический анализатор считывает первые символы и определяет первую лексему. Затем он передает эту лексему синтаксическому анализатору. Каждый раз, когда синтаксический анализатор получает лексему, он обрабатывает считываемую грамматическую конструкцию. На данном этапе он либо запрашивает у лексического анализатора еще одну лексему, либо, если он распознает оператор, вызывает генератор кода, чтобы тот создал последовательность соответствующих машинных команд. Затем эти машинные команды добавляются к объектной программе. Трансляция программы соответствует объектно-ориентированной парадигме программирования. Исходная программа, лексический анализатор, синтаксический анализатор, генератор кода и объектная программа являются объектами, которые взаимодействуют между собой, обмениваясь сообщениями, когда какой-либо из объектов приступает к выполнению своей задачи (рис. 11).
Рисунок 11 – Объектно-ориентированный подход к процессу трансляции программы
Компоновка и загрузка. Объектная программа, полученная в результате трансляции исходной программы, хотя и записана на машинном языке, не может быть сразу выполнена машиной. Одна из причин этого заключается в том, что большинство сред программирования позволяют создавать и транслировать модули программы как отдельные элементы и в разное время (что и обеспечивает модульную структуру программного обеспечения). Поэтому объектная программа, полученная после транслирования исходной программы, часто является только одним кусочком целой программы, каждой части которой для выполнения задачи необходимо взаимодействие с другими частями. Даже когда программа создается и транслируется как отдельная единица, ее объектную программу редко можно выполнить сразу, поскольку, скорее всего, она обращается к каким-либо обслуживающим программам, доступным через операционную систему, или к самой операционной системе. Поэтому объектная программа — это программа на машинном языке, которую, чтобы получить исполняемую программу, нужно соединить с другими объектными программами.
Эта задача выполняется программой, которая называется компоновщиком (linker). Компоновщик связывает объектные программы, полученные в результате трансляции, процедуры операционной системы и другое обслуживающее программное обеспечение и создает полную, исполняемую программу (иногда ее называют загрузочным модулем), которая сохраняется на запоминающем устройстве машины.
Наконец, для выполнения оттранслированной программы загрузочный модуль помещается в память программой, которая называется загрузчиком (loader) и часто является частью планировщика операционной системы. Этот этап очень важен в многозадачных системах. Программа использует память совместно с другими выполняемыми процессами, и количество этих процессов постоянно меняется, поэтому до момента выполнения программы неизвестно, какая область памяти ей доступна. Задача загрузчика состоит в том, чтобы поместить программу в область памяти, отведенную операционной системой, и в последний момент (в последнюю микросекунду) внести изменения, которые могут понадобиться, когда становятся известны адреса конкретных ячеек памяти. (Команда перехода должна переходить к необходимому оператору программы.) Желание свести эти последние изменения к минимуму привело к созданию методов, с помощью которых можно избежать явного обращения к ячейкам памяти в программе. В результате появились перемещаемые модули (relocatable module), которые выполняются правильно независимо от их размещения в памяти.
Таким образом, подготовка программы на языке высокого уровня к выполнению состоит из трех этапов: трансляции, компоновки и загрузки (рис. 12). После завершения трансляции и компоновки программу можно загрузить и выполнить, не возвращаясь к ее исходной версии. Однако если программу необходимо изменить, то все изменения производятся в исходной программе, которая затем снова транслируется и связывается. В результате создается новый загрузочный модуль, содержащий внесенные изменения.
Рисунок 12 – процесс подготовки программы к выполнению
Пакеты разработки программного обеспечения. Существует тенденция группировать транслятор и другие программы, использующиеся в процессе разработки программного обеспечения, в пакеты, которые функционируют как одна интегрированная система. Такую систему согласно предложенной нами классификации можно отнести к прикладному программному обеспечению. Используя такой пакет программ, программист получает доступ к редактору для написания программ, транслятору для перевода программы на машинный язык и большому количеству инструментов отладки, которые позволяют следить за выполнением программы и обнаруживать «глюки».
Использование такой интегрированной системы дает множество преимуществ. Самое очевидное из них заключается в том, что программист в процессе изменения и проверки программы с легкостью может переключаться между редактором и инструментами отладки. Кроме того, большинство пакетов разработки программного обеспечения позволяют связывать зависимые программные единицы, находящиеся в разработке, упрощая тем самым доступ к ним. Некоторые пакеты ведут учет того, какую программную единицу из группы зависимых изменили с момента последней контрольной точки. Такие возможности очень полезны при разработке больших систем программного обеспечения, когда множество взаимосвязанных элементов создаются разными программистами.
Редакторы в пакетах разработки программного обеспечения обычно соответствуют используемому языку программирования. Например, редактор в пакете разработки программного обеспечения обычно структурирует текст программы согласно правилам используемого языка и иногда распознает и автоматически дописывает зарезервированные слова, после того как программист ввел первые несколько символов. Во многих пакетах разработки программного обеспечения используется графический интерфейс, который позволяет программисту создавать программы из заранее написанных блоков, располагающихся на экране в виде значков. Выбранные блоки можно изменять с помощью редактора. Такие пакеты являются следствием всеобщего стремления создавать программы из больших написанных заранее блоков, а не писать их команда за командой.