Фундаментальные алгоритмы и структуры данных в Delphi
Шрифт:
– ---
Правило отладки № 1. Выбирайте воспроизводимый случай тестирования.
– ---
По нечеткому описанию проблемы можно обнаружить только самые простые ошибки. Тестовый случай, который при необходимости может воспроизвести ошибку, - это, по крайней мере, 90% на пути к ее обнаружению и устранению. Возможность воспроизведения ошибки позволяет с помощью отладчика определить место, где она возникает. Если же у вас нет теста, который может воспроизвести ошибку, то у вас нет и надежды.
Второе правило отладки намного сложнее.
– ---
Правило отладки № 2. Исходите из того, что ошибка внесена вами.
– ---
Может быть, вы неправильно используете API-интерфейс операционной системы или библиотека компонентов требует определенной последовательности
Следующее правило вытекает из уже рассмотренного нами материала.
– ---
Правило отладки № 3. Для проверки того, что код работает, как того ожидалось, используйте утверждения.
– ---
Кроме того, можно применять и протоколирование, которое позволит отслеживать состояние различных объектов.
А теперь перейдем собственно к отладке.
– ---
Правило отладки № 4. Используйте автоматизированные инструментальные средства отладки.
– ---
Возможно, ваша ошибка вызвана перезаписью какой-то области памяти или получением доступа к памяти после того, как она была освобождена, либо, скажем, при вызове API-функции не проверяется код возвращаемой ошибки. Все описанные типы проблем можно обнаружить в таком автоматизированном средстве отладки, как TurboPower Sleuth QA Suite. Приобретите средство отладки и используйте его не только в процессе тестирования, но и в процессе обнаружения ошибок.
Естественно, после этого следует сама отладка, которая может выполняться даже с помощью отладчика. Именно здесь наука программирования превращается в настоящее искусство. Отладку нельзя назвать алгоритмом, скорее, это соревнование. Единственным советом в этом соревновании может быть "не делайте никаких допущений". Если вы считаете, что некоторая переменная должна содержать определенное значение, проверьте это. Пользуйтесь средством визуализации значений отладчика. Для наблюдения за блоком памяти можно воспользоваться окном процессора. Попытайтесь предсказать значения переменных, а затем проверить свои предположения и при необходимости устраните ошибки. Без боязни добавляйте в код новые утверждения с целью проверки своих предположений.
Резюме
Эта глава была достаточно насыщена информацией и, честно говоря, была посвящена не столько алгоритмам и структурам данных, сколько технологиям увеличения быстродействия кода и процедурным методам.
Иногда быстродействие является результатом правильного выбора алгоритма или структуры данных (или того и другого). В других случаях увеличение скорости может быть достигнуто благодаря глубоким знаниям оборудования компьютера и операционной системы. Прежде всего, важно понимать, что единственным методом увеличения быстродействия приложения является использование профилировщика. Только предоставляемая профилировщиком статистика поможет определить, на что приложение тратит время, и лишь глубокое изучение отдельных блоков кода позволит оптимизировать приложение и увеличить его быстродействие. Хотелось бы подчеркнуть, что просто выбор "правильного" алгоритма или структуры данных отнюдь не означает, что приложение будет работать быстрее. Приведенная в книге информация поможет вам понять возможные способы ускорения выполнения отдельных частей кода, но только не нужно вносить в код сложные реализации алгоритмов там, где этого не требуется - вы только зря потеряете время.
Кроме того, в этой главе мы кратко рассмотрели тестирование и отладку - это тоже не алгоритмы, а скорее методы,
Глава 2. Массивы.
Несмотря на то что при стандартном (и не совсем стандартном) программировании используется огромное количество разного рода структур данных, большинство из них основаны на одном из двух фундаментальных контейнеров: массив и связный список. Если после прочтения этой книги вы научитесь правильно применять эти два типа структур, цель книги можно будет считать достигнутой. Они важны не только благодаря своей простоте, но и вследствие своей высокой эффективности. Массивы будут подробно рассмотрены в этой главе, а связные списки - в следующей. Кроме того, в главе 3 после связных списков будут описаны некоторые простые типы структур данных, основанные на этих двух фундаментальных типах. В главах 4 и 5, посвященных поиску и сортировке соответственно, мы также коснемся фундаментальных типов структур данных, но под несколько другим углом.
Невзирая на то что в книге будут приводиться полные реализации этих двух типов структур данных, иногда будет удобнее написать свою собственную реализацию. Поэтому важно четко понимать все аспекты, которые будут рассматриваться в этой и последующих главах.
Массивы
Во многих отношениях массивы являются простейшей структурой данных. Проще могут быть только такие базовые типы данных, как integer или Boolean. Массив (array) представляет собой последовательный список определенного количества элементов. Все элементы в массиве принадлежат к одному типу данных, и, как правило, хранятся в одном блоке памяти, т.е. каждый последующий элемент в памяти находится непосредственно после предыдущего. В таком случае говорят, что элементы массива являются смежными в памяти. Если ссылаться на элементы массива по их числовым индексам, то первый элемент будет иметь индекс 0 (или 1, или любое другое число, по крайней мере, в Delphi), значение индекса второго элемента будет больше на единицу и т.д. В коде элемент с индексом i обозначается как А[i], где А - идентификатор массива.
В Delphi имеется большой набор встроенных типов массивов. Кроме того, отдельные удобные типы массивов определены в библиотеке визуальных компонент VCL (Visual Component Library) в виде классов (и не только классов). Для поддержки таких классов, как массивы, разработчики Delphi предусмотрели возможность перегрузки операции массива, [], добавляя к нему новые свойства. Это единственная операция в Delphi, помимо + (сложение и конкатенация строк), которую можно перегружать.
Типы массивов в Delphi
В Delphi имеется три типа поддерживаемых языком массивов. Первый - стандартный массив, который объявляется с помощью ключевого слова array. Второй тип был впервые введен в Delphi 4 в качестве имитации того, что было давным-давно доступно в Visual Basic, - динамический массив, т.е. массив, длина которого может изменяться в процессе выполнения кода.
И последний тип массивов, как правило, не считается массивом, хотя в языке Object Pascal имеется несколько его вариаций. Конечно, мы говорим о строках: однобайтных строках (тип shortstring в 32-разрядной версии Delphi), строках с завершающим нулем (тип Pchar) и длинных строках в 32-разрядных версиях Delphi (которые имеют отдельную вариацию для "широких" символов).
Все массивы имеют одну и ту же структуру. Они состоят из одного или большего количества повторений другого типа данных, например, char, integer или record, которые в памяти находятся рядом друг с другом. Именно это последнее свойство стандартных массивов позволяет очень быстро получить доступ к отдельным элементам массивов. Весь процесс доступа к элементу сводится к простому вычислению адреса, для чего требуются, как мы вскоре увидим, всего несколько машинных инструкций.