Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 3491 / 369 | Оценка: 4.65 / 4.29 | Длительность: 30:37:00
Специальности: Программист, Менеджер
Лекция 11:

Отладка и оптимизация программ. Отладка

Объект Debug и его методы

Объект Debug специально сконструирован для проведения отладки. Он имеет два метода Print и Assert.

Метод Print

Его синтаксис:

Debug.Print список_выражений

Метод позволяет во время выполнения программы напечатать значения выражений из списка выражений в окне проверки Immediate. Трудно представить программу в период отладки, в которой бы многократно не использовался бы метод Print объекта Debug. Во всех наших примерах демонстрировалось его применение. Мы не будем останавливаться на некоторых деталях синтаксиса и управления выводом, при желании познакомиться с ними можно обратиться к справке.

Метод Assert

Его синтаксис:

Debug.Assert булево_выражение

Всякий раз, когда при выполнении программы управление получает метод Assert, вычисляется значение булевого выражения и, если оно истинно, выполнение программы продолжается обычным образом. Если же значения выражения ложно, то происходит останов выполнения и программа переходит в состояние прерывания и, как обычно, начинается отладка и выяснение причин, повлекших ложность высказывания, заданного Assert выражением.

Заметьте, Assert - выражения подобны одному из типов контрольных выражений, которые также приводят к прерыванию, когда приобретают значение False. Синтаксис и выполнение отдельного оператора Debug.Assert, совершенно понятны. Однако есть смысл поговорить о том, какую роль играют Assert - утверждения в процессе отладки. Идея такова: программа разбивается на участки, каждый из которых начинается и завершается Assert - утверждением. Предполагается, что программист может задать в виде булевого выражения утверждение, которое должно быть истинно в этой точке программы. В типичном случае Assert - утверждения вставляются в начало и конец каждой процедуры и функции. В начале процедуры эти утверждения задают условия, которым должны удовлетворять исходные данные. В конце работы процедуры булевы выражения описывает требования, предъявляемые к результатам. Если начальные и конечные утверждения истинны, то полагается, что процедура была корректно вызвана и корректно завершила свою работу. Конечно же, чтобы использовать эту технику в полной мере следует иметь опыт доказательства правильности программ. Приведем пример:

Public Function Fact2(ByVal N As Integer) As Integer
	'Функция спроектирована для вычисления факториалов чисел, не больших 7
	
	Debug.Assert (N >= 0) And (N < 8)
	
	If (N = 0) Or (N = 1) Then	 ' базис индукции.
		Fact2 = 1		' 0! =1.
	Else	' рекурсивный вызов в случае N > 0.
		Fact2 = Fact2(N - 1) * N
	End If
	
	Debug.Assert Fact2 <= 5040
	
End Function

Заметьте, говорить о том, правильно или неправильно работает функция Fact2, вычисляющая факториал, не имеет особого смысла, если не упоминать интервал возможных значений ее входного параметра. В узком интервале значений, для которого она и была спроектирована, она работает корректно. Вне этого интервала использовать ее не следует. Assert - утверждение в начале этой функции приостановит выполнение, если будет сделана попытка использовать эту функцию вне тех пределов, для которых она гарантирует корректную работу. Если бы этих утверждений не было, то попытка вызвать эту функцию с отрицательным N или N, большим 7, привела бы к останову выполнения программы.

Доказательство правильности программ

Мы уже говорили, что отладка, основанная на построении системы тестов, не может доказать правильность программы. Поэтому в теоретическом программировании были предприняты большие усилия по разработке методов доказательства правильности программ, такие же строгие, как и методы доказательства правильности теорем. На практике эти методы не получили широкого распространения по двум причинам. Во-первых, построить доказательство правильности программы сложнее, чем написать саму программу. Во-вторых, ошибки в доказательстве столь же возможны, как и в самой программе. Тем не менее, знание основ доказательства правильности программ должно быть частью образования программиста. Умение строго доказывать правильность простых программ помогает программисту лучше понять, как следует разрабатывать корректно работающие, сложные программы. Не ставя целью сколь либо полный обзор этого важного направления, остановимся лишь на самом понятии правильности программы. Действительно, мы многократно использовали этот термин, но что значит правильно (корректно) работающая программа? Вот одно из возможных определений. Пусть P(X,Y) - программа, с заданными входными данными X и результатами Y. Предикат Q(X), определенный на входных данных, будем называть предусловием программы P, а предикат R(X,Y), связывающий входные и выходные переменные будем называть постусловием программы P. Будем также предполагать, что в ходе своей работы программа не меняет своих входных переменных X.

Программа P(X,Y) корректна по отношению к предусловию Q(X) и постусловию R(X,Y), если из истинности Q(X) до начала выполнения программы следует, что, будучи запущенной, программа завершит свою работу и по ее завершению будет истинным предикат R(X,Y). Условие корректности записывают в виде триады (триады Хоора) - Q(X) {P(X,Y)} R(X,Y)

Уже из этого определения становится ясно, что говорить о правильности следует не вообще, а по отношению к заданным спецификациям, например, в виде предусловия и постусловия. Доказать правильность триады для сложных программ, как уже говорилось, довольно сложно. Один из методов (метод Флойда) состоит в том, что программа разбивается на участки, размеченные предикатами - Q1, Q2, …QN, R. Первый из предикатов представляет предусловие программы, последний - постусловие. Тогда доказательство корректности сводится к доказательству корректности последовательности триад:

Q1{P1}Q2;	Q2{P2}Q3;	…QN{PN}R

Нетрудно видеть, что введение Assert - утверждений является отражением метода Флойда. Хотя использование этих утверждений не предполагает проведения строгого доказательства, но это практически реализуемая попытка в этом направлении. Все что может быть сделано для повышения надежности программы, должно быть сделано.

Условная компиляция и отладка

Заметьте, что вызов методов объекта Debug может встречаться только в период отладки программы. В тот момент, когда программа передается конечному пользователю, вызов методов этого объекта не должен встречаться в работающей программе. Это понятно, поскольку в этом режиме никакие отладочные окна не появляются, нет никаких окон проверки и методу Print некуда направлять свой вывод, носящий отладочный характер. И уж тем более не должно прерываться выполнение программы, если вдруг Assert - утверждение станет ложным. Конечный пользователь просто не будет знать, что нужно делать в этом случае.

Когда отладка завершена, вызовы методов Print и Assert не должны встречаться в выполняемой программе. Конечно, можно просто удалить эти вызовы из текста программы или закомментировать их. Однако жизненный цикл многих программ таков, что снова и снова приходится возвращаться к отладке ранее разработанной программы, внесению заплаток, созданию новой версии, введению новых возможностей. Поэтому удаление или комментирование вызовов объекта Debug не является лучшим способом. Гораздо удобнее иметь возможность включать или выключать отладочный режим при необходимости. Для этой цели и используются средства условной компиляции. Рассмотрим их применение на примере вызовов методов объекта Debug. Идея состоит в том, что все вызовы методов этого объекта заключаются в обертку, заданную специальным оператором #If условной компиляции. Этот оператор проверяет истинность выражения, заданного, как правило, константой периода компиляции. Эта константа играет роль флажка, в зависимости от ее значения и будут выполняться отладочные операторы. По-видимому, достаточно было бы одного примера, но скажем об этом чуть подробнее.

Директива #const

Эта директива позволяет задать константы условной компиляции. Ее синтаксис:

#Const constname = expression

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

#If … Then … #Else директива

Оператор If условной компиляции или директива If имеет синтаксис, похожий на обычный оператор If:

#If expression Then
statements
[#ElseIf expression-n Then
[elseifstatements]]
[#Else
[elsestatements]]
#End If

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

Приведем простой пример использования средств условной компиляции:

'Константа conDebug включает или выключает отладочную печать
#Const conDebug = True
'#Const conDebug = False
Public Sub TestDebug()
	#If conDebug Then
		Debug.Print "Привет!"
	#Else
		MsgBox ("Привет!")
	#End If
End Sub
полина есенкова
полина есенкова
Дмитрий Вологжин
Дмитрий Вологжин
Добрый день, прошел тесты с 1 по 9, 10 не сдал, стал читать лекцию и всё пройденные тесты с 1 по 9 сбросились, когда захотел пересдать 10 тест.