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

1.1.6Динамические массивы указателей

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

Количество элементов массива определяется переменной SizeArr. Первоначально под массив указателей выделяется объем памяти, необходимый для хранения элементов массива, количество которых равно начальному значению SizeArrH. В последующем значение переменной SizeArr может увеличиваться.

Действительное количество элементов списка определяется переменной Count.

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

Отдельно следует остановиться на особенностях процесса удаления списка. Это вызвано тем, что список имеет двухуровневое построение – массив указателей на элементы списка и собственно набор элементов списка. В соответствии с этим будем различать удаление списка в процессе работы программы и удаление списка перед окончанием программы.

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

Удаление списка перед окончанием программы предусматривает удаление всего набора элементов списка и удаление массива указателей на элементы списка.

В языке С++ оператры new и delete могут использоваться для работы с динамическими массивами типизированых и нетипизированных указателей.

1.1.7Динамические массивы типизированных указателей

Для дальнейшего изложения используем следующие типы данных и переменные:

const SizeArrH = 4; /*начальный размер массива указателей*/

Delta = 10; /*приращение размера массива указателей*/

typedef struct ElmList

{

char Name[30];

int Age;

char Address[30];

} TElmList; /*тип для элемента списка */

void* PtrDynArrPtrElmList ; /*указатель на начало массива указателей*/

int Count; /*реальное количество элементов списка*/

int SizeArr; /*текущий размер массива указателей*/

Динамический массив в динамической памяти - куче (heap), идентифицируется указателем PtrDynArrPtrElmList типа void*, расположенным в статической памяти. Указатель PtrDynArrPtrElmList ссылается на первый байт блока байт, выделенного под этот массив. Все элементы динамического массива располагаются в куче последовательно и по тем же принципам, что и в статической памяти. Поэтому доступ к произвольному элементу динамического массива выполняется в два этапа.

На первом этапе выполняется приведение типа нетипизированного указателя PtrDynArrElmList к типизированому с помощью оператора приведения типа, который имеет следующий вид:

(тип) выражение;

Приведение нетипизированного указателя PtrDynArrElmList к типизированному указателю на массив указателей на элементы списка будет записано таким образом:

(TElmList **) PtrDynArrPtrElmList

Таким образом, получен доступ к массиву указателей на элементы списка.

На втором этапе выполняется доступ к требуемому элементу массива по его индексу.

Динамическая память

Элементы списка

Массив

Статическая

указателей

элемент № 1

п амять

на элементы

‑> на эл. № 4

элемент № 3

Указатель

‑> на эл. № 3

на массив

‑> на эл. № 2

элемент № 4

указателей

‑> на эл. № 1

PtrDynArrElmList

элемент № 2

Рисунок 1.12 – Структура списка на базе массива указателей

Примечание. Следует подчеркнуть, что в рассматриваемом случае непосредственно для работы с кучей типизированные указатели не используются. Здесь для работы с кучей используются нетипизированные указатели – указатели типа void*, а это позволяет применять такие указатели к данным любого типа.

Для работы с блоком байт, выделенным под массив, как с набором типизированных элементов типа TElmList*, возможен единственный подход:

  • преобразование беcтипового указателя PtrDynArrPtrElmList к указателю на типизированный указатель

(TElmList**) PtrDynArrPtrElmList

Обращение к элементу массива указателей на элементы списка. Пусть, например, необходимо получить доступ к элементу массива с индексом 5‑ть. Это выглядит следующим образом:

((TElmList **)PtrDynArrPtrElmList) [5].

Здесь сначала выполняется преобразование бестипового указателя PtrDynArrElmList на первый байт блока байт, выделенного для массива указателей, к типизированному указателю на массив указателей (TElmList**) PtrDynArrPtrElmList . Затем выполняется доступ к 5‑му элементу массива указателей

((TElmList**)PtrDynArrPtrElmList)[5]

а после этого выполняется доступ к соответствующему элементу списка

*(((TElmList**)PtrDynArrPtrElmList)[5])

Обращение к конкретному полю элемента списка. Пусть полям Name и Age элемента списка с индексом 5-ть необходимо присвоить некоторое значение. Это выполняется следующим образом:

((TElmList**)PtrArrPtrElmList)[5]->Age = 18;

strcpy(((TElmList**)PtrArrPtrElmList)[5]->Name, "Муха");

и, наоборот,

int d= ((TElmList**)PtrArrPtrElmList)[5]->Age;

Если идет обращение к конкретному полю структуры, то нет необходимости производить разыменование указателя на элемент при помощи оператора "*". Вместо этого, после того как получент доступ к указателю на элемент списка, к нему применяется оператор "->" и указывается название поля. Т.е. через указатель на структуру при помощи оператора "->" получаем доступ к ее полю.

Здесь сначала выполняется преобразование бестипового указателя PtrDynArrPtrElmList на первый байт блока байт, выделенного для массива указателей, к типизированному указателю на массив указателей (TElmList**)PtrDynArrPtrElmList. После чего выполняется раскрытие типизированного указателя на массив указателей и доступ к массиву указателей. Затем выполняется доступ к 5‑му элементу массива указателей ((TElmList**)PtrDynArrPtrElmList)[5], а затем выполняется доступ к соответствующему элементу списка ((TElmList**)PtrArrPtrElmList)[5]->. И, наконец, выполняется доступ к конкретному полю Name элемента списка ((TElmList**)PtrArrPtrElmList)[5]->Name.

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

указатель = new тип_элемента_массива[размер массива];

Например, необходимо выделить блок для SizeArr элементов типа TElmList*. Это достигается следующим образом:

PtrDynArrPtrElmList = new TElmList* [SizeArr].

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

delete [ ] PtrDynArrPtrElmList.

Автоматически освобождаются участки кучи того же размера, что и резервировались.

Примечание. Если вместо операторов new и delete используются функции malloc и free, то запрос памяти для динамического массива указателей будет иметь вид:

PtrDynArrPtrElmList = malloc( SizeArr * SizeOf(TElmList*)),

а освобождение памяти:

free (PtrDynArrPtrElmList).