Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3345

.pdf
Скачиваний:
2
Добавлен:
15.11.2022
Размер:
4.34 Mб
Скачать

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

Представление в памяти. Матрица в памяти размещается последовательно в соответствии с соблюдением упорядоченности по строкам: первыми располагаются элементы первой строки, затем второй и т.д. Дескриптор двумерного массива совершенно аналогичен дескриптору вектора (рис. 3.6) и включает следующие элементы: тип данных - матрица; нижняя граница 1-го индекса, верхняя граница 1-го индекса; нижняя граница 2-го индекса, верхняя граница 2-го индекса; тип элемента матрицы и длина элемента. Далее размещается первая строка, затем - вторая и т.д.

Доступ. Доступ к многомерному массиву может осуществляться с помощью формулы доступа, являющейся простым обобщением формулы, используемой для вектора.

Если А - матрица из М строк и N столбцов и при хранении А упорядочена по строкам, то адрес элемента A[I, J] определяется

выражением

 

адр А[I, J] = + (I - LB1)

S + (J - LB2) E,

где S - длина строки = (UB2 - LB2 + 1)

E,

LB1 - нижняя граница 1-го индекса;

LB2, UB2 - нижняя и верхняя граница 2-го индекса соответственно.

Собрав постоянные члены, можно получить более простое выражение

адр А[I, J] = - К + I S + J E, где К = LB1 S + LB2 E.

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

 

адр А[I, J] = ' + I S + J E,

где

' = - К.

131

3.4.Неоднородные массивы фиксированного размера: записи и структуры

Требование однородности, то есть одинаковости типов данных, объединяемых в массив, при решении практических задач часто является очень неудобным. Например, двумерный массив в виде таблиц всегда включает разнородные элементы, то есть вынуждает хранить в первом столбце данные одного типа, во втором - другого и т.д. В более общем случае линейный массив, состоящий из элементов разного типа, может, например, представлять личную карточку работника, включающую следующие элементы: фамилия, имя, отчество (это строки символов); год рождения, размер жалования (это числа); семейное положение и адрес места жительства (это строки символов) и т.д. Важны также совокупности карточек работников подразделений, учебных заведений, предприятий и т.п. Кроме того, группирование данных различных типов делается и для улучшения программы решения задачи (см. концепцию объектно-ориентированного программирования).

Для информационного отображения этих и подобных ситуаций более естественны неоднородные массивы фиксированного размера. Для таких массивов используются и другие названия: ЗАПИСЬ (RECORD), структура (ЯзПр Пл/1). Компоненты записей в них называются полями. Каждое поле идентифицируется именем и типом данных. Поэтому ЗАПИСЬ (ТАБЛИЦА) представляет собой НАБОР ИМЕНОВАННЫХ ЗАПИСЕЙ (ДАННЫХ) произвольной природы. Изучение указанного типа данных важно и потому, что эта структура напоминает «объект», составляющий основу объектно-ориентированного программирования, важнейшего на современном этапе.

Объединением разнородных данных в единый объект достигается возможность манипулирования ими как единым целым объектом сколь угодно сложной конструкции. Это очень ценное свойство ЯзПр. Манипуляция с объектами как целыми осуществляется при: ОПИСАНИИ (ДЕКЛАРИРОВАНИИ), ПРИСВАИВАНИИ и при ПЕРЕДАЧЕ в КАЧЕСТВЕ ПАРАМЕТРА в ПРОЦЕДУРУ или ФУНКЦИЮ. Если при этом необходимы детали (то есть части це-

132

лого), то определяется операция ВЫБОРКИ ПОЛЯ. Эта операция используется как при АНАЛИЗЕ всего объекта, так и при ПРИСВАИВАНИИ НОВЫХ ЗНАЧЕНИЙ отдельным полям структуры (ОБНОВЛЕНИЕ ДАННЫХ). Тип поля может быть любым стандартным (встроенным) или предварительно определенным программистом типом, включая и структурированные типы.

В отличие от однородных массивов на способ представления неоднородных массивов в памяти ЭВМ влияет наличие или отсутствие декларации. Рассмотрим эти случаи.

3.4.1. Неоднородный массив с декларацией

Содержание декларации такого массива и размещение его в памяти ЭВМ показано на рис. (рис. 3.8, с. 79). Логическая организация такого ЛИНЕЙНОГО МАССИВА совпадает с логической организацией ВЕКТОРА, с тем исключением, что элементы массива могут быть разного типа. При этом здесь используются индексы, не являющиеся целыми числами.

 

Неоднородный линейный

Тип данных

 

 

массив

 

 

 

 

 

 

 

 

 

 

3

 

 

 

 

 

Число элементов

 

 

 

 

 

 

CHAR

 

3

 

Дескриптор

 

 

 

 

 

 

 

 

 

 

FIX

 

1

Тип и длина каждого

 

 

 

 

 

 

 

 

элемента

 

FLOAT

 

2

 

 

 

 

 

 

 

NAME (ФИО,

 

Цепочка битов

 

EMPLOYE

AGE (возраст,

(следующий)

 

 

SALARY (оклад,

 

Рис. 3.7. Неоднородный линейный массив с полным дескриптором

133

Вместо целых чисел, используемых в качестве индексов при ссылке внутри вектора (А=(а1, а2, а3, ..., аn)), здесь появляются символические (уникальные) индексы, отличающиеся один элемент от другого (NAME, AGE, SALARY), то есть в отличие от однородного массива элементы данного массива не являются подобными порциями информации.

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

Структура памяти. Для представления в памяти линейных неоднородных массивов, как и в случае с однородными (с векторами), используется ПОСЛЕДОВАТЕЛЬНОЕ ПРЕДСТАВЛЕНИЕ: битовые цепочки, изображающие индивидуальные элементы, хранятся ПОСЛЕДОВАТЕЛЬНО в одном блоке памяти.

Доступ. Однако возможность ПРОИЗВОЛЬНОГО ДОСТУПА к элементам массива сохранилась. Основная форма доступа для неоднородного линейного массива выглядит следующим образом:

J 1

адр А J

длина А j

j 1

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

адр А J K J ,

 

 

J

1

где

K

J

длина A j .

 

 

 

 

 

j

1

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

134

3.4.2. Многомерные неоднородные массивы с декларациями

Переход от одномерных линейных массивов к многомерным основан на представлении, что каждый элемент линейного (однородного) массива может быть другим неоднородным линейным массивом (см. рис. 4.16 Д.Райли, с.149)*.

[1]

[2]

[3]

[4]

 

 

 

[N]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Сотрудники:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Фамилия

Поле 1; вектор из m символов

 

 

 

 

 

 

Имя

Поле 2; вектор из l символов

 

 

 

 

 

 

Отчество

Поле 3; вектор из k символов

 

 

 

 

 

 

Возраст

Поле 4;

цифра

 

 

 

 

Пол

Поле 5;

код «0» или «1»

 

 

 

 

Ученая степень

Поле 6; вектор из r символов

 

 

 

 

 

Должность

Поле 7; вектор из n символов

 

 

 

 

 

 

 

 

Рис. 3.8. Диаграмма массива «сотрудники»

* Дана ссылка на иногда используемое учебное пособие, считающееся в США, якобы, лучшим: Д.Райли. Абстракция и структуры данных. Вводный курс. М.:

Мир, 1993, 752 с.(5 т. экз.).

135

Поскольку каждый из этих подмассивов может иметь разные дескрипторы, а значит и разное число элементов разного типа, то результирующая структура будет иметь ФОРМУ ДЕРЕВА. При этом синтаксис декларации напоминает ОГЛАВЛЕНИЕ КНИГИ - основные заголовки, подзаголовки и т.д.

Как и многомерные однородные массивы, неоднородные массивы упорядочиваются по строкам. Индексы, обеспечивающие доступ к элементам, постоянны, поэтому вычисления, необходимые для доступа к любому элементу массива, могут быть выполнены заблаговременно (еще во время компиляции программы) и они сводятся к простому прибавлению константы к базовому адресу, указывающему местоположение структуры в памяти.

3.4.3. Неоднородные линейные массивы без декларации

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

Логическая организация. Последовательное ("плотное") представление для элементов массива, которые меняются по длине, не годится, ибо они будут "РАСТАЛКИВАТЬ" друг друга, хотя число этих элементов и не меняется, массив-то фиксированный по размеру (по числу элементов).Но все равно перестановка элементов (из-за их удлинения) при присваивании новых значений неизбежна.

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

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

136

те"). Если же дескрипторы занимают немного места, то они обычно хранятся вместе с указателями в векторе указателей (см. рис. 3.10, с. 83).

 

Неоднородный линейный массив

Тип данных

 

 

Дескриптор

 

Длина (число элементов)

4

массива

 

 

 

 

 

Нижняя граница

 

 

 

 

 

 

 

 

 

 

 

α

 

CH

 

Цепочка битов

 

 

 

 

 

 

 

 

 

J

 

(вектор указателей)

 

 

 

 

 

 

 

 

 

 

 

 

R

 

 

 

 

 

 

 

 

 

CH

 

 

 

 

 

 

Дескрипторы

элементов

Рис. 3.9. Неоднородный линейный массив с дескриптором, существующим во время выполнения программы

Для всего массива также необходим дескриптор, определяющий: тип данных - неоднородный линейный массив; число элементов и нижнюю границу диапазона изменения индекса, если она не предполагается равной 1.

Доступ. Представление массива в виде вектора УКАЗАТЕЛЕЙ упрощает доступ к индивидуальным элементам. Для доступа к A[J] точно так же, как для обычного вектора, вы-

137

числяется позиция J-го указателя в векторе указателей. Это указатель содержит адрес цепочки битов, представляющих значение элемента. Используются (в указателях) обычные целые индексы. Многомерное расширение неоднородного массива может быть получено путем использования техники однородных массивов по трансформированию в вектор векторов (но!) указателей, а не самих значений элементов массива. Сходная технология используется в массивах переменного размера, которые рассмотрим в следующей лекции.

3.5. Массивы переменного размера

Реальные задачи всегда решаются в условиях неопределенности. Поэтому любая фиксация чего-то (размера массива, типов входящих элементов и т.п.) является относительно реальности определенной "натяжкой". Это особенно важно для задач, использующих данные, поступающие в реальном масштабе времени. Поэтому в арсенале ЯзПр используются линейные массивы, которые могут динамически изменяться (расти и сокращаться по числу элементов) во время выполнения программы. Такие массивы имеют переменный размер.

Для таких массивов известно очень много названий, то есть их разновидностей. Вот некоторые из них: СТЕКИ, ОЧЕРЕДИ, СПИСКИ, МНОЖЕСТВА и т.д. При этом стек изменяется с одной стороны, очередь - с двух, список и множество - где угодно.

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

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

138

включения и исключения элементов. В массивах фиксированного размера можно получить индивидуальный доступ к каждому элементу с помощью ИНДЕКСА (в абсолютной! числовой шкале). Доступ к элементам массива переменного размера чаще всего относительный: найти элемент, следующий или предыдущий относительно данного, найти последний элемент и т.п. Доступ с помощью индекса менее важен, поскольку каждый элемент массива может изменять свою позицию при изменении размеров массива.

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

3.5.1. Стеки Являются простейшей разновидностью линейных массивов

переменной длины, изменяющейся в начале (голове) массива, но исключительно важными для компьютерной обработки. Имеются другие названия - пашдаун (push-down) список или магазин. Стек был разработан А.Тьюрингом в 1947 году. Он назвал его реверсивной памятью и использовал для связи подпрограмм.

Потребность в стеке при функционировании компьютера возникает очень(!) часто, например, при решении следующей задачи.

Пусть массив Т содержит в элементах T[0], T[1], ... запись АЛГОЛ-программы в соответствующих кодировках. То есть каждому символу программы ставится в соответствие его координата (индекс) в массиве Т. Задача состоит в том, чтобы найти все пары операторных скобок begin - end и построить таблицу, в которой каждой паре соответствует строка с указанием координат символа begin и соответствующего символа end.

Продвигаясь по записи программы от начала к концу, мы можем образовать очередь координат открывающих скобок begin, ожидающих соответствующих закрывающих скобок end. Особен-

139

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

Аналогичная задача возникает при управлении памятью, распределяемой автоматически в ходе выполнения задачи, что тоже разрешается стеком и т.п. Стек обеспечивает программам место для хранения и слежения за процессом работы. Наиболее важное использование стека – это запись адреса программы, откуда была вызвана подпрограмма, и параметров, передаваемых подпрограммам. Вызов всех ассемблерных подпрограмм осуществляется с помощью стека. Хотя любая часть программы может создать свой стек, но он чаще организуется один единственный.

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

Термин стек является описательным по отношению к способу, с помощью которого массив растет и сокращается.

В простейшем случае каждый элемент стека является одиночным числом. Такой стек можно отобразить на одномерный массив S. Операция заключения в стек значения х будет реализовываться процедурой В СТЕК (S,x); операция извлечения - процедурой ИЗ СТЕКА (S,x), где х - имя переменной, в которую заносится значение, извлекаемое (и одновременно удаляемое) из стека. Массив стека S будет начинаться с элемента S[0], который служит указателем очередного свободного места в стеке, то есть содержит индекс того элемента массива S, который следует за текущей вершиной. Если стек пуст, то S[0]=1.

140

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]