Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие часть 1.doc
Скачиваний:
131
Добавлен:
24.09.2019
Размер:
6.98 Mб
Скачать

1.2.2. Внутреннее представление структурированных типов данных

Переменные структурированных типов, как правило, занимают непрерывную область памяти, т. е. соседние элементы занимают соседние ячейки памяти. Это обеспечивает возможность быстрого вычисления адреса элемента по его индексу (совокупности индексов в многомерных массивах или имени поля в записях). Такой способ доступа к элементам называется прямым доступом. Множества не составляют исключения, например, в языке Pascal множества представлены в виде битового массива.

Для обеспечения прямого доступа к элементам данных структурированных типов обычно создается дополнительная структура, называемая дескриптором, которая хранит адрес начала, размер одного элемента и, возможно, некоторые другие данные, необходимые для быстрого вычисления адреса конкретного элемента данных.

Например, для одномерного массива адрес элемента ai определяется так:

ai=a0+i*sizeof(ТипЭлементовМассива),

если нумерация элементов массива начинается с нуля, где a— адрес первого байта памяти, занимаемой массивом.

1.2.3. Статическое и динамическое выделение памяти

Современные языки программирования предоставляют различные способы выделения памяти для данных, обрабатываемых программой. Возможны следующие варианты.

  • Память под переменную выделяется до начала выполнения программы в соответствии с описанием типа и остается неизменной до конца программы. Это внешние (глобальные) переменные и статические переменные. Термин «статические» пришел из языка С и обозначает переменные, имеющие ограниченную область видимости, но существующие от начала на конца работы программы.

  • Память выделяется при входе в определенный блок программы также в соответствии с описанием типа и освобождается при выходе из блока (автоматические переменные);

  • Память выделяется (захватывается) по запросу из программы и освобождается также по запросу. При этом размер памяти может определяться типом переменной или явно указываться в запросе. Такие переменные называются динамическими.

Первые два способа называют статическим выделением памяти (не путать со статическими переменными в смысле языка С). Во многих случаях статическое выделение памяти ведет к ее неэффективному использованию, особенно для структурированных типов данных, т. к. не все выделенная область памяти реально заполняется данными и обрабатывается. Поэтому во многих языках есть удобные средства динамического формирования переменных. Возможности создания и использования динамических переменных тесно связаны с механизмами указателей, поскольку динамическая переменная не имеет статически заданного имени, и доступ к такой переменной возможен только через указатель.

Отметим важную особенность — при любом способе формирования память для переменной выделяется сразу одной непрерывной областью. Например, нельзя сначала выделить память под половину массива, а затем под оставшуюся половину, даже если пользоваться динамически формируемыми массивами. Прямой доступ к элементам достается дорогой ценой.

    1. Абстрактные типы данных (атд)

      1. Понятие атд

Набор стандартных типов во всех языках программирования ограничен. При этом операции, которые можно связать с каждым стандартным типом, жестко фиксированы в самом языке и не могут быть изменены программистом так, чтобы они стали частью этого типа. Заметим, что производные типы данных позволяют ограничить множество значений базового типа, но не переопределить операции. Структурированные типы данных также не являются исключением, поскольку набор стандартных средств для доступа к отдельным элементам составного значения жестко фиксирован.

Однако при решении самых разнообразных задач возникает потребность в новых типах данных, связанных со своими операциями, которые не определены для стандартных типов. В других случаях появляется необходимость изменить реализацию встроенных операций, что, увы, невозможно. Идя навстречу пожеланиям прикладных программистав, разработчики компиляторов могли бы расширять набор встроенных типов в новых версиях, но есть стандарт языка, который довольно консервативен. Кроме того, нельзя расширять набор встроенных типов до бесконечности.

Выход из этой ситуации состоит в предоставлении программисту возможности конструировать свои собственные типы, связывая с ними любой набор операций. В связи с этим в программировании появилось понятие абстрактного типа данных (АТД).

Этот термин появился в 1974 г. в статье Б.Лисков и С.Зиллеса [22], посвященной принципам разрабатывавшегося ими языка программирования CLU. Термин быстро распространился среди программистов и теоретиков программирования, хотя на самом деле абстрактные типы не более абстрактны, чем стандартные типы (точнее, стандартные типы так же абстрактны, как и АТД). Иногда используется термин «типы, определяемые пользователем», но и он не совсем точно отражает суть понятия, поскольку все типы, кроме встроенных, так или иначе определяются пользователем.

Обсудим смысл этого понятия. АТД, как и обычный тип данных, определяет множество значений и множество операций, применимых к этим значениям. Однако, в отличие от стандартных типов, реализация операций для абстрактного типа не встроена в язык программирования, а является частью определения АТД. При этом прикладные программы (они называются клиентами АТД) используют абстрактные типы так же, как стандартные типы, т.е. только через набор операций, определенных для этого АТД, и не имеют никакого доступа к его реализации.

Важно отметить, что таким образом можно расширять до бесконечности набор стандартных типов, добавляя именно те типы, которые нужны для решения тех или иных конкретных задач.

Подводя итог сказанному, можно считать АТД основными строительными конструкциями при разработке алгоритмов. В [12] АТД трактуются как исполнители. Имея заданный набор исполнителей с четко определенным набором операций, можно существенно сократить время разработки алгоритмов, при этом повысить надежность разработанного программного обеспечения.

АТД является полным, если он обеспечивает достаточно операций для того, чтобы все требующиеся пользователю действия с переменными этого типа (назовем их объектами) могли быть проделаны с приемлемой эффективностью. Полнота типа зависит от контекста использования. Если тип предполагается использовать в ограниченном контексте (например, одна программа), то должно быть обеспечено достаточно операций для этого контекста.

Если тип предназначен для общего использования, желательно иметь полный набор операций [13], который должен включать 3 класса различных операций.

  1.  Примитивные конструкторы. Эти операции создают объекты соответствующего типа, не используя никаких объектов в качестве аргумента. Конструктор предшествует другим операциям.

  2. Конструкторы и(или) модификаторы. Эти операции используют в качестве аргументов объекты соответствующего им типа и создают другие объекты такого же типа либо модифицируют заданные объекты. Это группа операций, которая используется для выполнения основной работы с объектами.

  3. Селекторы и(или) индикаторы. Эти операции используют в качестве аргументов объекты соответствующего им типа и возвращают результат другого типа (например, логического). Они используются для получения информации об объектах или получения отдельных элементов составного значения, но никак не изменяют объекты.

Например, для простых целочисленных типов операции сложения и вычитания являются конструкторами, а операции инкремента и декремента — модификаторами, операции сравнения при этом являются индикаторами.

Задача определения необходимого набора операций АТД является очень ответственной. К счастью, имеется довольно обширный, хорошо проработанный набор АТД широкого назначения, которые считаются стандартными. Они хорошо освещены в литературе, поэтому их назначение и смысл операций понятны. Во многих языках имеются библиотеки, содержащие набор стандартных АТД универсального назначения, например, библиотека STL в C++, библиотека VCL в Delphi и т.д..

В следующих разделах стандартные АТД будут рассмотрены достаточно подробно. Отметим, что сложные АТД могут строиться на основе более простых, используя их так же, как и стандартные типы данных.