Отладка программы

 

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

В процессе отладки программы выделяются этапы:

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

 

Этот код еще не готов к выполнению: для этого требуется его «ориентация» на те адреса основной памяти, где он будет выполняться.

 

Следует отметить, что при интерпретации анализу подвергается не вся программа, а отдельные операторы. При этом из названных шагов выполняются лишь первые четыре.