Тестирование-книга. Ю. Н. Артеменко Научный редактор
Скачать 6.27 Mb.
|
выполнить одно действие ELSE выполнить другое действие Пример: IF VAR > 5 THEN SET VAR_2 = 20 ELSE SET VAR_2 = 10 Команда, следующая за ключевым словом THEN (SET VAR_2 = 20), выполняется только при условии, что VAR > 5. В противном случае выпол няется команда, следующая за ключевым словом ELSE (SET VAR_2 = 10). Условный оператор может и не включать предложения ELSE. В этом слу чае при невыполнении условия, следующего за ключевым словом IF, уп равление передается команде, следующей за условной конструкцией. Неправильное сравнение (т.е. > вместо >=) Проверяемое условие (VAR > 5) может быть логически неверным. В частности, программисты нередко забывают о ситуациях, когда значения двух переменных равны. 5 1 2 Часть III: Управление проектами и группами Неверные результаты сравнений Предположим, что программист хотел проверить, равны ли между со бой значения трех переменных. Для этого он написал IF (VAR + VAR_2 + VAR_3) / 3 = VAR. Если значения трех переменных равны, их среднее арифметическое будет равно первому из этих чисел. Более того, для большинства не рав ных значений это условие не выполнится. Но не для всех! Если значения переменных равны 2, 1 и 3, тогда (2+1+3)/3=2. Однако числа 2, 1 и 3 разные. Подобные сокращения и усовершенствования, применяемые вме сто самых естественных проверок ((VAR = VAR_2) AND (VAR_2 = VAR_3)), являются источниками множества ошибок. Неравенство против равенства в случае выбора из трех вариантов Третий вариант выбора часто добавляется в программу в ходе ее под держки и модификации. Первоначально переменная VAR могла принимать только значения 0 и 1, позднее добавилось еще значение 2. В исходной программе проверка IF VAR = 0 выполнялась прекрасно. Второй вариант выбора обрабатывался предложением THEN. Входящие в него операторы предназначались для случая VAR = 1 и теперь, вероятно, требуют модифи кации. Сравнение значений переменных с плавающей запятой Вычисления над данными с плавающей запятой всегда чреваты ошиб ками, прежде всего связанными с округлением и усечением результата. Например, из-за небольшой ошибки результат, который должен быть рав ным 0, оказывается равным 0,00000008. Расхождение небольшое, но про верку на равенство нулю (IF VAR = 0) такой результат не пройдет. Спутаны включающее и исключающее ИЛИ Во многих операторах IF проверяется истинность хотя бы одного из нескольких условий (IF A OR В THEN...). Однако существует два вида операторов OR (ИЛИ): • включающее ИЛИ: результат выражения A OR В истинен, если исти нен хоть один из операндов; • исключающее ИЛИ: результат выражения A OR В истинен, если истинен один из операндов, но не оба сразу; Приложение: Распространенные программные ошибки 5 1 3 Неверное отрицание логического выражения Иногда оператор IF принимает форму IF А не истинно THEN. Иногда программисты применяют оператор логического отрицания неверно или не задумываются, что означает получившееся выражение. Например, IF NOT (A OR В) THEN означает IF (A = FALSE) AND (В = FALSE) THEN. Для выполнения следующего за THEN оператора необходимо, чтобы ни А ни В не были истинны. Присваивание вместо сравнения В языке С конструкция if (VAR = 5) означает, что переменной VAR присваивается значение 5, а затем выполняется проверка равенства резуль тата нулю. Программисты часто пишут так вместо if (VAR == 5), что означает просто проверку равенства значения переменной пяти. Путаница происходит из-за похожести операторов сравнения (==) и присваивания (=). Команды, входящие в предложение THEN или ELSE Вот пример типичной ошибки. IF VAR = VAR_2 THEN SET VAR_2 = 10 SET VAR_2 = 20 Очевидно, что команда SET VAR_2 = 20 должна была бы входить в предложение ELSE. Иначе в приведенном фрагменте кода оператор IF вообще ни к чему. Каким бы ни было его условие, переменная VAR_2 все равно получит значение 20. Команды, которые не входят ни в одно из предложений Иногда программист по ошибке включает в предложение THEN или ELSE команды, которые должны выполняться при любом значении усло вия оператора IF. Если эти команды повторяются в обоих предложениях, это просто потеря времени, а если только в одном — это уже ошибка уп равления потоком. Не проверен флаг Пусть, например, программа вызывает подпрограмму, которая должна присвоить значение определенной переменной, а та не делает этого, уста новив флаг ошибки. Программа не проверяет этот флаг, а действует так, будто все нормально, и проверяет значение переменной в операторе IF. Дальнейшие действия программы могут оказаться правильными только по 17 5 1 4 Часть III: Управление проектами и группами счастливой случайности. Ведь подпрограмма сообщила (посредством уста новки флага), что переменная не содержит правильного значения. Не сброшен флаг Когда подпрограмма была вызвана в предыдущий раз, она установила флаг ошибки. Теперь она вызвана снова, и первым делом должна сбросить этот флаг. Однако по ошибке программиста подпрограмма этого не дела ет. В результате программа игнорирует ее совершенно правильные резуль таты из-за того, что флаг ошибки так и остался установленным еще с прошлого раза. Многочисленные варианты Оператор IF предполагает только два варианта: выражение либо истин но, либо ложно. В тех же случаях, когда возможных вариантов действий программы более двух, используются такие операторы, как CASE, SWITCH, SELECT и оператор перехода на основе вычисляемого значения. Вот эквивалент типичного оператора данного вида. IF VAR = 1 DO Задание_1 IF VAR = 2 DO Задание_2 IF VAR = 3 DO Задание_3 IF VAR имеет любое другое значение DO Задание_для_остальных_случаев Задание для остальных случаев не обязательно должно присутствовать. Пропущен блок, выполняемый во всех остальных случаях Программист может ограничить набор действий перечислением конк ретных условий и забыть написать, что же делать, если ни одно из усло вий не выполнится. Переменная VAR может принять непредусмотренное значение либо из-за ошибки в программе, либо вследствие ее последующей модификации. Наличие блока обработки нестандартного значения поможет выявить такие ситуации и предотвратить дальнейшие ошибки. В этом блоке программа может, например, выводить неожиданное значение переменной на экран. Неверно определены действия для всех остальных случаев Предположим, что программист полагает, что переменная VAR может принимать только четыре значения. Он явно указывает три из них, а чет вертое обрабатывает как "остальные". Но будут ли запрограммированные на этот случай действия правильными, если переменная примет пятое или шестое значение? Приложение: Распространенные программные ошибки 5 1 5 Пропущенные варианты Переменная VAR может принимать пять значений, но программист о пятом забыл. Требуется подразделение одного варианта на несколько Иногда одним из условий оператора выбора могут охватываться вари анты, требующие различной обработки. Например, определены действия для условия VAR<30, но на самом деле при условии VAR<15 программа должна делать одно, а при условии 15<=VAR<30 — другое. Как правило, такие ошибки допускаются в последнем блоке, предназначенном для всех остальных случаев. Пересекающиеся условия Рассмотрим следующий фрагмент кода. IF VAR > 5 DO Задание_1 IF VAR > 7 DO Задание_2 Первое из указанных условий включает в себя второе. Если переменная примет значение 9, они оба будут истинны — какое же задание должна будет выполнить программа? Это будет зависеть от использованного опе ратора выбора и языка программирования, но, скорее всего, имеет место просто ошибка программиста. Как правило, условия не должны пересекать ся. Неверные условия и невозможные случаи Программа выполняет Задание_16, только когда (VAR < 6) AND (VAR > 18). Это означает, что Задание_16 не выполняется никогда. Подобным же образом программист может определить условие, которое на практике никогда не выполнится, даже если теоретически в нем нет ничего невоз можного. Проблемы такого рода видны только при анализе исходного кода. Тем не менее, подобные безобидные ошибки затрудняют чтение програм мы специалистом, которому предстоит ее поддерживать в дальнейшем, удлиняют код и создают путаницу. Ошибки обработки или интерпретации данных Данные могут передаваться от одной части программы к другой и от одного процесса к другому. В ходе передачи данные могут быть искажены или неверно интерпретированы. 5 1 6 Часть III: Управление проектами и группами Проблемы при передаче данных между подпрограммами Программа вызывает подпрограмму и передает ей данные, например, таким образом: DO SUB (VAR_1, VAR_2, VAR_3) Все три переменные, VAR_1, VAR_2 и VAR_3, передаются основной программой подпрограмме. Они называются параметрами подпрограммы. Подпрограмма может обращаться к этим переменным по другим именам. Первая строка определения подпрограммы может быть такой: SUB (INPUT_1, INPUT _2, INPUT _3) Подпрограмма получает первую переменную из списка (VAR_1) и на зывает ее INPUT_1. Вторую переменную (VAR_2) она называет INPUT_2, третью (VAR_3) - INPUT_3. Определения переменных в программе и подпрограмме должны совпа дать. Если VAR_1 определена как целая, такой же должна быть и INPUT_1. Параметры указаны не в том порядке или пропущены В программе стоит команда DO SUB (VAR_1, VAR_2, VAR_3), а под программа связывает INPUT_1 с VAR_2 , a INPUT_2 с VAR_1. Програм мисты часто путают порядок параметров подпрограмм, из-за чего возможны самые разнообразные ошибки. Пропуск параметров менее распространен, поскольку не все языки программирования допускают несоответствие числа параметров и выявля ют такие ошибки еще на этапе компиляции. Несоответствие типов данных Предположим, что переменные VAR_1 и VAR_2 определены в програм ме как двухбайтовые целые. При этом в подпрограмме параметры INPUT_1 и INPUT_2 определены как однобайтовые целые. Возможность такой ситуации и действия программы в этом случае определяются языком программирования. Вполне возможно, что в переменной INPUT_1 окажет ся первый байт значения VAR_1, а в переменной INPUT_2 — второй байт значения VAR_1. Тип данных определяет способ их хранения и обработки. Простейши ми примерами типов данных могут служить целые числа, числа с плаваю щей запятой и строки символов. Более сложными типами данных являются массивы, записи и массивы записей. Существует и множество других — стеки, деревья, связанные списки и т.д. Приложение: Распространенные программные ошибки 5 1 7 Иногда несоответствие типов данных в вызывающей и вызываемой процедурах является намеренным. Например, вызывающая программа может передать трехмерный массив, который процедура будет интерпрети ровать как одномерный массив с большим количеством элементов. Анало гичным образом переданный массив символов может интерпретироваться как массив чисел. Некоторые языки программирования не допускают по добных вещей, в других же это считается стандартным и очень удобным приемом программирования. Однако этот прием требует от программиста большой аккуратности, и его беспорядочное применение приводит к огром ному количеству ошибок. Как правило, когда общий размер передаваемых и получаемых данных в байтах не совпадает, это уже явная ошибка с до вольно неприятными последствиями. Псевдонимы и различная интерпретация содержимого одной и той же области памяти Если два разных имени относятся к одной и той же области памяти, они называются псевдонимами. Если VAR_1 и XX являются друг для дру га псевдонимами, после выполнения программой команды SET XX = 20 значением переменной VAR_1 также становится 20. Некоторые псевдонимы используются еще хитрее. Пусть переменные VAR_1 и VAR_2 являются однобайтовыми целыми, а XX — двухбайтовым целым, первый байт которого совпадает с переменной VAR_1, а второй — с переменной VAR_2. В этом случае, если присвоить переменной XX зна чение 20, переменная VAR_1 получит значение 0, а переменная VAR_2 — 20. О существовании псевдонима может забыть даже автор программы, а уж программисту, которому придется ее поддерживать в дальнейшем, ничего не стоит его вовсе не заметить. В результате программист может изменить значение одной переменной, не ожидая, что это повлечет за собой и изме нение значения другой. Если же комбинации псевдонимов более сложные — ждите множества ошибок. Неправильная интерпретация данных Программа передает подпрограмме значение температуры по шкале Цельсия, а та интерпретирует его как температуру по Фаренгейту. Другой пример: подпрограмма присваивает флагу ошибки значение 1, желая его сбросить. Программа понимает это значение как "флаг установлен". Неадекватная информация об ошибке Столкнувшись с ошибкой, подпрограмма не установила ее флаг. Или же она выдала сигнал ошибки без сопутствующей информации, в результате чего вызывающая программа не знает, как ее обрабатывать. 5 1 8 Часть III: Управление проектами и группами Перед аварийным выходом из подпрограммы не восстановлено правильное состояние данных Подпрограмма обнаруживает ошибку или нестандартную ситуацию и завершает свою работу. Желательно, чтобы перед этим она восстановила нормальное состояние данных, которое могла изменить. Устаревшие копии данных Два процесса могут иметь собственные копии общих данных. Так же может быть и с двумя подпрограммами внутри одного и того же процесса. Когда данные меняются, обе копии должны быть обновлены. Однако не редко оказывается, что один из процессов по-прежнему работает со стары ми данными, поскольку другой забыл сообщить об их изменении. Связанные переменные не синхронизированы Одна переменная обычно содержит удвоенное значение второй, но в определенный момент первая изменилась, а вторая — нет. Эта ошибка чаше всего возникает при взаимодействии подпрограмм. Локальная установка глобальных данных Глобальная переменная определяется в главной программе. Подпрограм мы могут ею пользоваться — читать и изменять ее значение. Однако изме нения подпрограммами значения глобальной переменной часто происходят случайно. Программист имел в виду не глобальную переменную, о суще ствовании которой он мог вообще забыть, а локальную переменную про цедуры с тем же именем. Глобальное использование локальных переменных Переменная называется локальной для некоторой процедуры, если ни какая другая процедура не может ее использовать. Различие между глобаль ными и локальными переменными существует во многих языках программирования, однако не во всех. Например, в старых версиях языка BASIC все переменные были глобальными. Если программист, пишущий на таком языке, не будет соблюдать предельную аккуратность в наименовании переменных, он может ненамеренно обратиться к локальной переменной из другого места программы. Неверная маска битового поля Чтобы сэкономить несколько байтов или микросекунд, некоторые про цессы могут пользоваться битовыми полями. Каждый байт такого поля Приложение: Распространенные программные ошибки 5 1 9 хранит восемь переменных — по одной на каждый бит. Можно также использовать пару битов под одну переменную, еще три под другую и т.д. Маской называется битовый шаблон, позволяющий программисту работать только с интересующими его битами. Если маска неверна, значит програм мист обращается не к той переменной. Неверное значение из таблицы Данные часто организованы в виде таблиц (простейшими примерами могут быть массивы и записи). Переменная-указатель определяет, какой элемент таблицы будет записываться или считываться. Программа может обратиться не к тому элементу таблицы, или же ее элемент может содер жать неправильное значение. Границы расположения данных Программа может пользоваться неправильным адресом начала или кон ца набора данных. Не обозначен конец нуль-терминированной строки Пусть STRING_VAR является строковой переменной. В ней может содержаться строка Привет или строка Я — строковая переменная, а мо жет быть, нечто гораздо более длинное. Если количество хранящихся в строковой переменной символов не фиксировано, должен быть какой-ни будь индикатор конца строки. Распространенным решением является по мещение в конец строки нуль-символа (все его биты нулевые). Этот символ называется терминатором строки. В тех языках программирования, в кото рых используются нуль-терминаторы, все процедуры, работающие со стро ками, ищут этот символ. Однако по ошибке его может не оказаться (его забыли, поверх него что-то записали или не скопировали его из исходной переменной). Подпрограмма, печатающая такую строку, будет печатать все содержимое памяти до тех пор, пока не найдет нуль-символ или не достиг нет конца памяти. Возможно, впрочем, она сразу выдаст сообщение об ошибке. При копировании такой переменной в другое место памяти так же возможны неприятности с затиранием нужных данных. Неожиданный конец строки Предполагается, что в переменной STRING_VAR должна храниться строка Я — строковая переменная, однако подпрограмма, отображающая ее содержимое на экране, выводит только Я — стр. Это может означать, что нуль-символ случайно скопирован в середину строки. Если же длина стро ки хранится в отдельном байте (байте длины), возможно, что это значение неправильно вычислено или испорчено. |