В оригинальном С компилятор не мог проверить, соответствуют ли параметры, переданные в вызове функции, списку параметров в определении функции; прототипов не было. Это часто вело к неуловимым ошибкам, поскольку ошибочный вызов функции мог вызывать лишь частично ошибочные результаты, которые проходили незамеченными во время тестирования, или мог даже вообще не появиться во время тестирования. Например:
if (argc < 2)
fprintf ("usage: %s [ options ] files\n", argv[0]);
/* отсутствует stderr */
Если программа, содержащая этот фрагмент, никогда не вызывается с ошибочным числом аргументов, fprintf()
, в которой отсутствует первый аргумент FILE*
, также никогда не вызывается.
Программа V7 lint
была предназначена для решения таких проблем. Она делала два прохода через все файлы программы, сначала собирая сведения об аргументах функций, а затем сравнивая вызовы функций с собранной информацией. Особые файлы «библиотеки lint
» предоставляли сведения о функциях стандартных библиотек, так что их также можно было проверить, lint проверяла также другие сомнительные конструкции.
С появлением в стандартном С прототипов необходимость в lint
уменьшилась, но не исчезла совсем, поскольку C89 все еще допускает объявления функций в старом стиле.
extern int some_func(); /* Список аргументов неизвестен */
Вдобавок, многие другие аспекты программы можно проверять
Программа splint
(Secure Programming Lint — Lint для безопасного программирования)[186] является современным обновлением lint
. Она предусматривает слишком много опций и возможностей, чтобы перечислять их здесь, но ее стоит исследовать.
Следует знать об одной особенности подобных lint
программ, которая заключается в том, что они выводят целый поток предупреждающих сообщений. Многие из сообщаемых предупреждений в действительности безвредны. В таких случаях инструменты допускают использование специальных комментариев, сообщающих. «Да, я знаю об этом, это не проблема», splint
лучше всего работает, когда вы предоставляете в своем коде большое количество подобных примечаний.
splint
является мощным, но сложным инструментом; выделение некоторого времени на изучение его использования, а затем частое его использование поможет сохранить ваш код ясным.
Разработка программного обеспечения содержит элементы и искусства, и науки, это одна сторона того, что делает ее такой восхищающей и стимулирующей профессией. Данный раздел вводит в тему тестирования программного обеспечения, которая также включает в себя и искусство, и науку; таким образом, это несколько более общий и высокий уровень (читай: «на который можно махнуть рукой»), чем остальная часть данной главы.
Тестирование программ является неотъемлемой частью процесса разработки программного обеспечения. Весьма маловероятно, что программа заработает правильно на 100 процентов при первой компиляции. Программа не несет ответственности за свою правильность; за это отвечает
Один из способов классификации различных видов тестов следующий:
Это тесты, которые вы пишете для каждого модуля или функционального компонента своей программы. В качестве части работы может потребоваться также создать
Важно спроектировать тесты для каждого функционального компонента
Это тесты, которые применяются, когда все функциональные компоненты были написаны, протестированы и отлажены по отдельности. Идея в том, что все затем помешается на свое место в каркасе и тестируется все в целом, чтобы убедиться, что взаимодействия между компонентами работают.
Неизбежно вы (или ваши пользователи!) обнаружат проблемы. Это могут быть действительные ошибки, или ограничения дизайна, или неизбежные отказы в «пограничных случаях». Когда вы смогли воспроизвести и исправить проблему, сохраните первоначальные условия отказа в качестве возвратного теста.