Отладка программы
Это самый трудоемкий этап. Его цель – проверка синтаксической и логической правильности программы, а также определение того, что программа функционирует на всем диапазоне допустимых данных.
В процессе отладки программы выделяются этапы:
1. трансляция исходного текста программы;
2. компоновка программы;
3. выполнение программы с целью определения логических ошибок;
4. тестирование программы.
Трансляция
При трансляции выполняется перевод программы, понятной человеку, на язык, понятный компьютеру. Если цель трансляции – преобразование всего исходного текста на внутренний язык компьютера (т.е. получение некоторого нового кода) и только, то такая трансляция называется также компиляцией. Исходный текст называется также исходной программой или исходным модулем, а результат компиляции – объектным кодом или объектным модулем. Если же трансляции подвергаются отдельные операторы исходных текстов и при этом полученные коды сразу выполняются, такая трансляция называется интерпретацией. Поскольку трансляция выполняется специальными программными средствами, последние носят название компилятора или интерпретатора, соответственно.
В процессе компиляции последовательно выполняются лексический, синтаксический, семантический анализ, генерация промежуточного кода, оптимизация промежуточного кода, генерация внутреннего представления.
Лексический анализ
Выявляются отельные составляющие текста программы, которые называются лексемами, и определяется их тип. К числу лексем относятся названия операторов, например, read или write; имена (переменных или констант), например, NABOR или CHISLO, различные разделители, такие как круглые скобки, знаки препинания и т.д. Одновременно типами указанных лексем являются названия операторов, имена переменных, разделители и т.д. Если программистом допущена ошибка и оператор ввода указан, например, rread, он будет распознан компилятором как имя. На этом этапе выявляется также использование недопустимых языком программирования символов, например, символа @. В результате лексического анализа исходная программа кодируется: каждая лексема заменяется кодом ее типа, что сокращает объем текста программы. Кроме того, из текста программы удаляются пробелы.
Пример 1. Лексический анализ фрагмента программы на Турбо-Паскале, содержащего только один правильно записанный оператор ввода:
read (NABOR,CHISLO);
дает цепочку
1 2 3 4 3 2 4 ,
где 1 – код названия оператора;
2 – код скобки;
3 – код имени;
4 – код знака препинания.
Пример 2. Лексический анализ фрагмента программы на Турбо-Паскале, содержащего только один неправильно записанный оператор ввода:
rread (NABOR,CHISLO); ,
дает цепочку
3 2 3 4 3 2 4 .
Синтаксический анализ
Определяется синтаксическая правильность закодированной в результате лексического анализа цепочки лексем. Например, определяется парность скобок, наличие в нужном месте требуемого знака препинания, соответствие исходного текста структурным правилам того или иного языка программирования, средствами которого была составлена программа. Для решения задачи в исходной (закодированной) цепочке выделяются подструктуры, соответствующие некоторым фрагментам оператора. Например, для оператора ввода в Турбо-Паскале можно определить структуру, представленную на рисунке 1 и соответствующую правилам из примера1 раздела Структурно-стилизованный способ описания алгоритма:
Рисунок 1
В то же время структура "правильного" оператора ввода определяется схемой:
Рисунок 2
Поскольку представленные структуры идентичны, оператор read (NABOR,CHISLO); при синтаксическом анализе определяется как правильный.
Именно на этом шаге выявляются ошибки в написании названий операторов: они ведут к некорректной структуре всего оператора. Так, примеру с "неправильным" оператором ввода соответствует структура, показанная на рисунке:
Рисунок 3
Поскольку структура этого рисунка не соответствует структуре рисунка 1, оператор rread (NABOR,CHISLO); расценивается как синтаксически некорректный: компилятор не найдет в анализируемом фрагменте требуемого действия, которое задается названием оператора, и сообщит об этом программисту.
Семантический анализ
На этом шаге выявляются ошибки, допущенные программистом в нарушение правил составления программ, например, следующего вида: все переменные и константы перед употреблением в операторах языка должны быть описаны; каждое имя (переменной или константы) должно быть описано только один раз; требуется согласование типов переменных с использующими их функциями и т.д. Так, если вся программа состоит только из оператора
read (NABOR,CHISLO); ,
она будет определена как семантически некорректная, поскольку в ней отсутствуют описания вводимых переменных.
Пример 3. Программа
var NABOR, CHISLO: integer;
read (NABOR, CHISLO);
расценивается семантическим анализатором как семантически корректная.
Пример 4. Программа
var CHISLO: integer;
CHISLO:=CHISLO + 1;
расценивается семантическим анализатором как семантически корректная.
Генерация промежуточного кода
Закодированная цепочка лексем преобразуется в некоторое промежуточное представление, принятое на том или ином компьютере, например, в программу на языке ассемблера (для простоты используем некоторый условный ассемблер).
Пример 5. Программа из примера 4 преобразуется в промежуточный код на условном ассемблере:
$1 DD ? ; операторы DD описывают переменные: каждый из них
$2 DD ?; отводит под переменную 2 байта памяти;
$3 DD ?; переменные $1 - $3 вводятся генератором кода как
CHISLO DD ?; вспомогательные переменные;
MOVE 1, $1; в операторах MOVE константы или содержимое
MOVE CHISLO, $2; переменных слева от запятой помещаются в переменные и
MOVE $1, AX; регистры процессора (АХ и СХ), указанные справа;
MOVE $2, CX;
ADD AX, CX; выполняется сложение содержимого регистров АХ и СХ,
; результат остается в регистре АХ
MOVE AX, $3; выполняются перемещения значений, находящихся в
MOVE $3, CHISLO; регистре и переменной, в соответствующие переменные
Полученный код избыточен: в самом деле, выполняется бессмысленная пересылка константы 1 и значения переменной CHISLO сначала в дополнительные переменные $1 и $2, а затем уже в регистры АХ и СХ, которые и используются при сложении. Аналогичные бессмысленные действия выполняются и с результатом сложения. Однако уменьшение размеров кода осуществляется на следующем этапе.
Оптимизация промежуточного кода
Из программы, полученной на предыдущем шаге, устраняются «лишние» операторы, переменные и константы, использование которых не влияет на корректность выполняемых действий.
Пример 6. Оптимизация промежуточного кода для программы из примера 5 приводит ее к виду:
CHISLO DD ?
MOVE 1, AX
MOVE CHISLO, CX
ADD AX, CX
MOVE AX, CHISLO
Такой результат связан с тем, что ряд операторов выполняли лишние перемещения данных из одного места хранения в другое. Удаление этих операторов повлекло удаление вспомогательных переменных.
Генерация объектного кода
Программа, полученная в результате оптимизации, преобразуется в машинный код (так называемый объектный модуль), в котором использованы относительные, а не абсолютные адреса основной памяти. Как правило, для получения объектного модуля применяется условная память с начальным адресом 1. Каждый оператор программы преобразуется в машинную команду и размещается, начиная с начального адреса, в той последовательности, в которой он следует в программе. При этом учитывается размер каждого оператора. Пусть командам программы из примера 6 соответствуют машинные команды:
код объем действие
операции
124 1 б сложить содержимое регистров АХ и СХ, результат – в регистре АХ,
125 3 б поместить содержимое регистра АХ по адресу,
126 2 б поместить константу в регистр АХ,
127 3 б поместить содержимое адреса в регистр СХ.
Оператор DD выделяет объем памяти в 2 байта для описываемой переменной. Тогда схема размещения объектного кода имеет вид, представленный в таблице:
Адрес (байт) | Команда | Комментарий | |
внутреннее представление | объем (байт) | ||
- | Память для переменной CHISLO – результат действия команды DD | ||
126 1 | В регистр АХ помещается 1 | ||
127 1 | Содержимое адреса 1 помещается в регистр СХ | ||
Соответствует действию CHISLO + 1 | |||
125 1 | Содержимое регистра АХ помещается по адресу 1, т.е. выполняется действие CHISLO = CHISLO+1 |
Этот код еще не готов к выполнению: для этого требуется его «ориентация» на те адреса основной памяти, где он будет выполняться.
Следует отметить, что при интерпретации анализу подвергается не вся программа, а отдельные операторы. При этом из названных шагов выполняются лишь первые четыре.