- •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. Компиляция "налету"
- •Дополнительная литература
- •Интерфейсы
- •Отладка
- •Тестирование
- •Производительность
- •Переносимость
8.7. Переносимость и внесение усовершенствований
Одним из наиболее огорчительных источников проблем переносимости является изменение системного программного обеспечения за время жизненного цикла. Изменения могут затронуть любой интерфейс системы, приводя к неоправданной несовместимости версий.
При изменении спецификации изменяйте и имя. Наш любимый (если можно так выразиться) пример — изменение свойств команды Unix echo, которая в изначальном виде предназначалась для простого вывода аргументов:
% echo hello, world
hello, world
%
Однако со временем эта команда стала ключевой частью многих скриптов оболочки, и перед ней встала необходимость генерировать форматированный вывод. Теперь echo стала некоторым образом интерпретировать аргументы, то есть стала неким аналогом printf:
% echo 'hello\nworId'
hello
world
%
Новые возможности, конечно, полезны, но из-за них у всех скриптов, использующих echo в изначальном варианте, возникли проблемы с совместимостью. Поведение
% echo $PATH
стало зависеть от того, какая из версий echo используется. Если переменная случайно содержит обратную косую черту (что вполне может произойти в DOS или Windows), то echo попытается интерпретировать ее. Это похоже на разницу в выводе через printf (str) и printf("%s", str) в случае, если переменная str содержит знак процента.
Мы привели только часть истории про echo, но уже то, что сказано, иллюстрирует основную проблему: изменения в системе приводят к появлению версий программ с преднамеренно различным поведением, что создает непреднамеренные проблемы с переносимостью. И исправить эти ошибки зачастую оказывается непросто. Проблем было бы гораздо меньше, если бы новая версия echo получила и новое имя.
Приведем еще один пример. В Unix существует команда sum, которая выводит размер файла и его контрольную сумму. Предназначена эта команда для проверки правильности передачи данных:
% sum file
52313 2 file
%
% копируем file на другую машину
%
% telnet othermachine
$
$ sum file
52313 2 file
$
После передачи контрольная сумма не изменилась, так что с хорошей вероятностью можно считать, что передача прошла успешно.
Система разрасталась, появлялись новые версии, и в какой-то момент кто-то решил, что алгоритм вычисления контрольной суммы не идеален, и sum была изменена с использованием лучшего алгоритма. Кто-то еще пришел к тому же выводу и тоже изменил sum, реализовав другой, столь же хороший алгоритм, и т. д. В результате сейчас имеется несколько версий sum, каждая из которых выдает свой вариант ответа. Мы поставили эксперимент, скопировав некий файл на другие машины, чтобы выяснить, какие же результаты покажет sum в каждом конкретном случае:
% sum file
52313 2 file
%
% копируем file на машину 2
% копируем file на машину 3
% telnet machine2
$
$ sum file
eaaOd468 713 file
$ telnet machines
>
> sum file
62992 1 file
>
Непонятно, произошел сбой в передаче или просто мы столкнулись с разными версиями sum. Может быть и то, и другое.
Таким образом, sum являет собой яркий пример препятствия на пути к переносимости: программа, призванная помогать в копировании файлов с одной машины на другую, имеет несколько несовместимых версий, что делает ее абсолютно непригодной для использования.
Для выполнения изначально поставленной задачи первая версия sum вполне подходила: алгоритм, заложенный в ней, был не самым эффективным, но приемлемым. Ее "улучшение", может, и сделало собственно команду лучше, но зато использовать ее по назначению стало нельзя. И дело тут, надо сказать, не в том, что получилось несколько разных по существу команд, а в том, что все эти команды имеют одно и то же имя. Как видите, проблема несовместимости версий может оказаться весьма серьезной.
Поддерживайте совместимость с существующими программами и данными. Когда выпускается новая версия программы, например текстового редактора, то она, как правило, умеет читать файлы, созданные предыдущей версией. При этом мы ожидаем, что из-за добавления новых возможностей формат должен измениться. Но зачастую новые версии оказываются не в состоянии обеспечить способ записи в предыдущем формате. Пользователи новых версий, даже если они не обращаются к добавленным возможностям, не могут применять свои файлы совместно с пользователями более старой версии, таким образом, обновлять программы приходится сразу всем. Независимо от того, чем определяются такие решения — технической необходимостью или маркетинговой политикой, — о таких случаях можно только сожалеть.
Совместимостью сверху вниз называется возможность программы соответствовать спецификациям своих более ранних версий. Если вы собираетесь изменить свою программу, убедитесь, что при этом вы не создадите противоречий со старыми версиями и связанными с ними данными. Тщательно документируйте изменения и продумайте способ восстановить первоначальные возможности. И главное, задумайтесь над тем, перевесят ли достоинства предлагаемых вами усовершенствований потери от непереносимости, которые при этом возникнут.
