- •1. Стиль 10
- •3. Проектирование и реализация 63
- •4. Интерфейсы 85
- •5. Отладка 115
- •6. Тестирование 134
- •7. Производительность 157
- •8. Переносимость 180
- •9. Нотация 203
- •Введение
- •Брайан в. Керниган
- •1.1. Имена
- •1.2. Выражения
- •Упражнение 1 -6
- •1.3. Стилевое единство и идиомы
- •1.4. Макрофункции
- •1.5. Загадочные числа
- •1.6. Комментарии
- •1.7. Стоит ли так беспокоиться?
- •Дополнительная литература
- •2.1. Поиск
- •2.2. Сортировка
- •2.3. Библиотеки
- •2.4. Быстрая сортировка на языке Java
- •2.5. "О большое"
- •2.6. Динамически расширяемые массивы
- •2.7. Списки
- •Упражнение 2-8
- •2.8. Деревья
- •Упражнение 2-15
- •2.10. Заключение
- •Дополнительная литература
- •Проектирование и реализация
- •3.1. Алгоритм цепей Маркова
- •3.2. Варианты структуры данных
- •3.3. Создание структуры данных в языке с
- •3.4. Генерация вывода
- •3.5.Java
- •Into the air. When water goes into the air it
- •3.7. Awk и Perl
- •3.8. Производительность
- •3.9. Уроки
- •Дополнительная литература
- •4. Интерфейсы
- •4.1. Значения, разделенные запятой
- •4.2. Прототип библиотеки
- •4.3. Библиотека для распространения
- •Упражнение 4-4
- •4.5 Принципы интерфейса
- •4.6. Управление ресурсами
- •4.7. Abort, Retry, Fail?
- •4.8. Пользовательские интерфейсы
- •Дополнительная литература
- •5. Отладка
- •5.1. Отладчики
- •5.2. Хорошие подсказки, простые ошибки
- •5.3, Трудные ошибки, нет зацепок
- •5.4. Последняя надежда
- •5.5. Невоспроизводимые ошибки
- •5.6. Средства отладки
- •5.7. Чужие ошибки
- •5.8. Заключение
- •Дополнительная литература
- •6. Тестирование
- •6.1. Тестируйте при написании кода
- •6.2. Систематическое тестирование
- •6.3. Автоматизация тестирования
- •6.4. Тестовые оснастки
- •6.5. Стрессовое тестирование
- •6.6. Полезные советы
- •6.7. Кто осуществляет тестирование?
- •6.8. Тестирование программы markov
- •6.9. Заключение
- •Дополнительная литература
- •7.Производительность
- •7.1. Узкое место
- •7.2. Замеры времени и профилирование
- •7.3. Стратегии ускорения
- •7.4. Настройка кода
- •7.5. Эффективное использование памяти
- •7.6. Предварительная оценка
- •7.7. Заключение
- •Дополнительная литература
- •8. Переносимость
- •8.1. Язык
- •8.2. Заголовочные файлы и библиотеки
- •8.3. Организация программы
- •8.4. Изоляция
- •8.5. Обмен данными
- •8.6. Порядок байтов
- •8.7. Переносимость и внесение усовершенствований
- •8.8. Интернационализация
- •8.9. Заключение
- •Дополнительная литература
- •9.1. Форматирование данных
- •9.2. Регулярные выражения
- •Упражнение 9-12
- •9.3. Программируемые инструменты
- •9.4. Интерпретаторы, компиляторы и виртуальные машины
- •9.5. Программы, которые пишут программы
- •9.6. Использование макросов для генерации кода
- •9.7. Компиляция "налету"
- •Дополнительная литература
- •Интерфейсы
- •Отладка
- •Тестирование
- •Производительность
- •Переносимость
5.8. Заключение
При правильном подходе отладка может превратиться в удовольствие типа разгадывания головоломки. Впрочем, неважно, нравится нам это или нет, отладка является искусством, которое нам придется демонстрировать регулярно. Было бы неплохо, если бы ошибок не возникало вовсе, поэтому мы с самого начала пытаемся избежать их, стараясь писать правильный код. Хорошо написанный код сразу содержит меньше ошибок, а те, что все же имеются, гораздо легче обнаружить.
После того как вы увидите ошибку, первое, что нужно сделать, — понять, на что "намекает" эта ошибка. Откуда она могла взяться? Есть ли в ней что-нибудь знакомое? Не менялось ли что-нибудь в программе буквально только что? Есть ли какие-нибудь особенности у входных данных, которые привели к ошибке? Нескольких хорошо отобранных тестовых случаев и нескольких операторов печати в коде может быть достаточно.
Если четких намеков нет, все равно, хорошо подумать — лучший первый шаг, за которым должны следовать систематические попытки локализовать местонахождение проблемы. Одним из возможных шагов будет сокращение входных данных до минимальных размеров, при которых программа все еще отказывается работать. Другой возможный шаг — удаление кода, чтобы устранить те его участки, что не связаны с проблемой. Можно добавить проверяющий код, который включается только через определенное количество шагов в программе, чтобы опять же попытаться локализовать проблему. Все эти шаги делаются в рамках одной стратегии "разделяй и властвуй", при отладке эффективной столь же, сколь в политике и войне.
Используйте другие вспомогательные средства. Объясните свой код кому-нибудь еще (хотя бы плюшевому медведю) — это восхитительно эффективно. Используйте отладчик, чтобы увидеть стек вызовов. Используйте коммерческие средства обнаружения утечек памяти, нарушения границ массивов, подозрительного кода и т. п. Пройдитесь по программе, если станет ясно, что вы не очень понимаете, как она работает.
Познайте себя и то, какие ошибки вы совершаете. После того как вы нашли и обнаружили ошибку, убедитесь, что вы устранили и другие подобные ошибки. Подумайте о происшедшем, чтобы избежать повторения той же самой ошибки.
Дополнительная литература
Много полезных советов по отладке содержится в книгах Стива Ма-гьюира "Создание надежного кода" (Steve Maguire. Writing Solid Code. Microsoft Press, 1993) и Стива Мак-Коннелла "Все о коде" (Steve McConnell. Code Complete. Microsoft Press, 1993).
6. Тестирование
В практике вычислений вручную или с помощью настольной машины надо
взять за правило проверять каждый шаг вычисления и, при нахождении
ошибки, локализовать ее, повторив процесс в обратном порядке с той точки,
где ошибка была обнаружена впервые.
Норберт Винер. Кибернетика
Тестирование и отладка часто упоминаются вместе, однако это две разные вещи. Сильно упрощая, можно сказать, что отладкой называется то, что вы делаете, когда знаете, что программа не работает. Тестирование же — это последовательные, систематические попытки добиться ошибки от программы, которая считается работающей.
Эдсгеру Дейкстре (Edsger Dijkstra) принадлежит известное высказывание о том, что тестирование может показать лишь наличие ошибок, но не их отсутствие. Он надеется на то, что создатели программ смогут писать их корректно, то есть без ошибок вообще, и, следовательно, в тестировании не будет никакой необходимости. Это, конечно, отличная цель, в и к ее достижению стоит стремиться, но для настоящих (коммерческих) программ это пока нереально. Так что в данной главе мы остановимся на том, как тестировать программы с целью находить ошибки быстро, рационально и эффективно.
Задумываться о потенциальных проблемах вашего кода полезно всегда. Систематическое тестирование, от простейших до самых хитроумных тестов, позволит удостовериться в том, что программа является корректной с самого начала и остается таковой по мере усовершенствования. Автоматизация позволяет во многом избежать нудного ручного тестирования, заменив его экстенсивным автоматическим тестированием. Существует великое множество приемов и ухищрений, которым опыт научил программистов.
Один из способов написания кода, не содержащего ошибок, — генерировать его программно. Если некоторое задание на программирование понятно настолько, что работа по написанию кода кажется механической, ее следует механизировать. Так бывает, когда программу можно сгенерировать из спецификации, написанной на специализированном языке. Например, мы компилируем код на языке высокого уровня в ассемблерный код, используем регулярные выражения для задания шаблонов текста, используем нотации типа SUM(A1: A50) для представления операций в некотором диапазоне ячеек электронной таблицы. В подобных случаях при наличии корректного генератора или транслятора и корректной спецификации результирующая программа будет также корректна. Более детально эту обширную тему мы обсудим в главе 9, в этой же главе мы в общих чертах осветим способы создания тестов из компактных спецификаций.
