ВУЗ: Томский государственный университет систем управления и радиоэлектроники
Категория: Учебное пособие
Дисциплина: Проектирование информационных систем
Добавлен: 21.10.2018
Просмотров: 10789
Скачиваний: 8
106
Остальные
участники
также
приобретают
опыт,
рассматривая
ошибки
и
стиль
программирования
других
программистов.
Наконец,
инспекция
является
способом
раннего
выявле-
ния
наиболее
склонных
к
ошибкам
частей
программы,
позво-
ляющим
сконцентрировать
внимание
на
этих
частях
в
процессе
выполнения
тестирования
на
ЭВМ
(один
из
принципов
тести-
рования
п.
6.2).
6.3.3 Список вопросов для выявления ошибок при
инспекции
Важной
частью
процесса
инспектирования
является
про-
верка
программы
на
наличие
общих
ошибок
с
помощью
списка
вопросов
для
выявления
ошибок.
Концентрация
внимания
в
предлагаемом
списке
на
рассмотрении
стиля,
а
не
ошибок
(во-
просы
типа
«Являются
ли
комментарии
точными
и
информа-
тивными?»
и
«Располагаются
ли
операторы
THEN/ELSE
и
DO/END
по
одной
вертикали
друг
под
другом?»)
представляет-
ся
неудачной.
Также
неудачным
представляется
и
наличие
не-
определенности
в
списке,
уменьшающее
его
полезность
(вопро-
сы
типа
«Соответствует
ли
текст
программы
требованиям,
вы-
двигаемым
при
проектировании?»).
Список,
приведенный
в
данном
разделе,
был
составлен
после
многолетнего
изучения
ошибок
программного
обеспечения.
В
значительной
мере
он
является
независимым
от
языка;
это
означает,
что
большинство
ошибок
встречается
в
любом
языке
программирования.
6.3.3.1 Ошибки обращения к данным
1.
Существуют
ли
обращения
к
переменным,
значения
ко-
торым
не
присвоены
или
не
инициализированы?
Наличие
пере-
менных
с
не
установленными
значениями
—
наиболее
часто
встречающаяся
программная
ошибка,
она
возникает
при
раз-
личных
обстоятельствах.
Для
каждого
обращения
к
единице
данных
(например,
к
переменной,
элементу
массива,
полю
в
структуре)
попытайтесь
неформально
«доказать»,
что
ей
при-
своено
значение
в
проверяемой
точке.
107
2.
Не
выходит
ли
значение
каждого
из
индексов
за
грани-
цы,
определенные
для
соответствующего
измерения
при
всех
обращениях
к
массиву?
3.
Принимает
ли
каждый
индекс
целые
значения
при
всех
обращениях
к
массиву?
Нецелые
индексы
не
обязательно
явля-
ются
ошибкой
для
всех
языков
программирования,
но
пред-
ставляют
практическую
опасность.
4.
Для
всех
обращений
с
помощью
указателей
или
пере-
менных-ссылок
память,
к
которой
производится
обращение,
уже
распределена?
Наличие
переменных-ссылок
представляет
собой
ошибку
типа
«подвешенного
обращения».
Она
возникает
в
ситуациях,
когда
время
жизни
указателя
больше,
чем
время
жизни
памяти,
к
которой
производится
обращение.
Например,
к
такому
результату
приводит
ситуация,
когда
указатель
задает
локальную
переменную
в
теле
процедуры,
значение
указателя
присваивается
выходному
параметру
или
глобальной
перемен-
ной,
процедура
завершается
(освобождая
адресуемую
память),
а
программа
затем
пытается
использовать
значение
указателя.
Как
и
при
поиске
ошибок
первых
трех
типов,
попытайтесь
не-
формально
доказать,
что
для
каждого
обращения,
использую-
щего
переменную-указатель,
адресуемая
память
существует.
5.
Если
одна
и
та
же
область
памяти
имеет
несколько
псевдонимов
(имен)
с
различными
атрибутами,
то
имеют
ли
значения
данных
в
этой
области
корректные
атрибуты
при
об-
ращении
по
одному
из
этих
псевдонимов?
Ошибки
типа
некор-
ректных
атрибутов
у
псевдонимов
могут
возникнуть
при
ис-
пользовании
атрибута
DEFINED
или
базированной
памяти
в
PL/1,
оператора
EQUIVALENCE
в
Фортране,
глагола
REDEFINES
в
Коболе,
записей
с
вариантами
в
Паскале
или
объединений
(UNION)
в
языке
Си.
В
качестве
примера
можно
привести
программу
на
языке
Си,
содержащую
вещественную
переменную
A
и
целую
переменную
B.
Обе
величины
размеще-
ны
на
одном
и
том
же
месте
памяти
(т.е.
помещены
в
одно
и
то
же
объединение).
Если
программа
записывает
величину
A,
а
обращается
к
переменной
B,
то,
вероятно,
произойдет
ошибка,
поскольку
машина
будет
использовать
битовое
представление
числа
с
плавающей
точкой
в
данной
области
памяти
как
целое.
108
6.
Отличаются
ли
типы
или
атрибуты
переменных
вели-
чин
от
тех,
которые
предполагались
компилятором?
Это
может
произойти
в
том
случае,
когда
программа
на
PL/1
или
Коболе
считывает
записи
из
памяти
и
обращается
к
ним
как
к
структу-
рам,
но
физическое
представление
записей
отлично
от
описания
структуры.
Или
программа
на
языке
Си,
допускающем
произ-
вольные
преобразования
типов,
содержит
переменную-указа
-
тель
на
структуру
типа
T
1
,
инициализированную
указателем
на
структуру
типа
T
2
(не
наследуемую
от
T
1
).
7.
Существуют
ли
явные
или
неявные
проблемы
адреса-
ции,
если
в
машине
будут
использованы
единицы
распределе-
ния
памяти,
меньшие,
чем
единицы
адресации
памяти?
Напри-
мер,
в
PL/1
в
системе
IBM
S/370
битовые
строки
фиксирован-
ной
длины
не
обязательно
начинаются
с
границ
байтов,
а
адре-
сами
могут
быть
только
границы
байтов.
Аналогично,
в
языке
Си
размер
отдельных
полей
в
структуре
может
задаваться
в
би-
тах.
Если
программа
вычисляет
адрес
битового
поля
и
впослед-
ствии
обращается
к
нему
по
этому
адресу,
то
может
произойти
ошибочное
обращение
к
памяти.
Такое
же
положение
может
сложиться
при
передаче
подпрограмме
(процедуре)
битового
поля
в
качестве
аргумента.
8.
Если
используются
указатели
или
переменные-ссылки,
то
имеет
ли
адресуемая
память
атрибуты,
предполагаемые
ком-
пилятором?
Примером
несоответствия
атрибутов
может
слу-
жить
случай,
когда
указатель
PL/1,
по
которому
базируется
структура
данных,
имеет
в
качестве
значения
адрес
другой
структуры.
9.
Если
к
структуре
данных
обращаются
из
нескольких
процедур
или
подпрограмм,
то
определена
ли
эта
структура
одинаково
в
каждой
процедуре?
10.
Не
превышены
ли
границы
строки
при
индексации
в
ней?
11.
Существуют
ли
какие-нибудь
другие
ошибки
в
опера-
циях
с
индексацией
или
при
обращении
к
массивам
по
индексу?
109
6.3.3.2 Ошибки описания данных
1.
Все
ли
переменные
описаны
явно?
Отсутствие
явного
описания
не
обязательно
является
ошибкой,
но
служит
общим
источником
беспокойства.
Так,
если
в
подпрограмме
на
Фор-
тране
используется
элемент
массива
и
отсутствует
его
описание
(например,
в
операторе
DIMENSION),
то
обращение
к
массиву
(например,
Х
=
А(1)),
будет
интерпретироваться
как
вызов
функции.
Последнее
приведет
к
тому,
что
машина
будет
пы-
таться
обработать
массив
как
программу.
Если
отсутствует
яв-
ное
описание
переменной
во
внутренней
процедуре
или
блоке,
следует
ли
понимать
это
так,
что
описание
данной
переменной
совпадает
с
описанием
во
внешнем
блоке?
2.
Если
не
все
атрибуты
переменной
явно
присутствуют
в
описании,
то
понятно
ли
их
отсутствие?
Например,
отсутствие
атрибутов,
считающееся
общепринятым
в
PL/1,
часто
является
источником
неожиданных
осложнений.
3.
Если
начальные
значения
присваиваются
переменным
в
операторах
описания,
то
правильно
ли
инициализируются
эти
значения?
Во
многих
языках
программирования
присвоение
начальных
значений
массивам
и
строкам
представляется
до-
вольно
сложным
и,
следовательно,
является
возможным
источ-
ником
ошибок.
4.
Правильно
ли
для
каждой
переменной
определены
длина,
тип
и
класс
памяти
(например,
STATIC,
AUTOMATIC,
BASED
или
CONTROLLED
в
PL/1;
AUTO,
CONST,
VOLATILE,
REGISTER,
STATIC
в
Си
и
т.д.)?
5.
Согласуется
ли
инициализация
переменной
с
ее
типом
памяти?
Например,
если
в
подпрограмме
на
Фортране
необхо-
димо
устанавливать
начальные
значения
переменной
каждый
раз
при
вызове
подпрограммы,
переменная
должна
быть
ини-
циализирована
в
операторе
описания,
отличном
от
оператора
DATA.
Если
в
PL/1
описывается
инициализация
величины
и
начальное
значение
необходимо
устанавливать
каждый
раз
при
вызове
процедуры,
то
для
этой
переменной
должен
быть
опре-
делен
класс
памяти
АUTОМАТIС,
а
не
STATIC.
6.
Есть
ли
переменные
со
сходными
именами
(например,
VOLT
и
VOLTS)?
Наличие
сходных
имен
не
обязательно
явля-
110
ется
ошибкой,
но
служит
признаком
того,
что
имена
могут
быть
перепутаны
где-нибудь
внутри
программы.
6.3.3.3 Ошибки вычислений
1.
Имеются
ли
вычисления,
использующие
переменные
недопустимых
(например,
неарифметических)
типов
данных?
2.
Существуют
ли
вычисления,
использующие
данные
разного
вида?
Например,
сложение
переменной
с
плавающей
точкой
и
целой
переменной.
Такие
случаи
не
обязательно
явля-
ются
ошибочными,
но
они
должны
быть
тщательно
проверены
для
обеспечения
гарантии
того,
что
правила
преобразования,
принятые
в
языке,
понятны.
Это
особенно
важно
для
языков
со
сложными
правилами
преобразования
(например,
для
PL/1,
Си).
Например,
следующий
фрагмент
программы
на
PL/1:
DECLARE A BIT(1);
A=1;
определяет
значение
A
равным
битовому
0,
а
не
1.
Или
в
языке
Си
попытка
присвоить
переменной
с
плавающей
точкой
значе-
ние
1/2
даст
значение
0
(т.к.
1
и
2
имеют
целый
тип).
3.
Существуют
ли
вычисления,
использующие
перемен-
ные,
имеющие
одинаковый
тип
данных,
но
разную
длину?
Та-
кой
вопрос
справедлив
для
PL/1,
и
возник
он
из
этого
языка.
Например,
в
PL/1
результатом
вычисления
выражения
25
+
1/3
будет
5.333...,
а
не
25.333
…
Аналогично,
в
языке
Си
присутст-
вуют
множество
данных
одного
типа,
но
имеющих
разную
дли-
ну.
Например,
это
целые
типы
(CHAR,
INT,
LONG,
SHORT,
INT64
и
т.д.),
вещественные
типы
(FLOAT,
DOUBLE,
LONG
DOUBLE)
и
т.д.
К
тому
же,
эти
типы
могут
быть
как
знаковыми
(SIGNED),
так
и
беззнаковыми
(UNSIGNED).
4.
Имеет
ли
результирующая
переменная
оператора
при-
сваивания
атрибуты,
описывающие
ее
с
меньшей
длиной,
чем
в
атрибутах
выражения
в
правой
части?
5.
Возможны
ли
переполнение
или
потеря
результата
во
время
вычисления
выражения?
Это
означает,
что
конечный
ре-
зультат
может
казаться
правильным,
но
промежуточный
ре-