- •Содержание
- •1 Списковые структуры и динамическая память 10
- •2 Списковые структуры и файлы 79
- •3 Мультисписковые структуры 121
- •4 Указатели на функции 134
- •5 Совместное использование указателей на данные и указателей на функции 141
- •6 Оформление курсового проекта 157
- •Введение
- •1Списковые структуры и динамическая память
- •1.1Представления однонаправленных списков массивами указателей на элементы списка
- •1.1.1Представление однонаправленных списков массивами указателей
- •1.1.2Статические массивы указателей
- •1.1.3Операции со списками в статических массивах указателей
- •1.1.4Статические массивы указателей в динамической памяти
- •1.1.5Операции со списками в статических массивах указателей в динамической памяти
- •1.1.6Динамические массивы указателей
- •1.1.7Динамические массивы типизированных указателей
- •1.1.8Операции со списками в динамических массивах типизированных указателей
- •1.1.9Динамические массивы нетипизированных указателей
- •1.1.10Операции со списками в динамических массивах нетипизированных указателей
- •1.1.11Продолжение. Динамические массивы нетипизированных указателей
- •1.1.12Замечания по оператору &
- •1.1.13Нетипизированые указатели и массивы
- •1.1.14Нетипизированые указатели и память
- •1.1.15Продолжение. Динамические массивы нетипизированных указателей
- •2Списковые структуры и файлы
- •2.1Бестиповые файлы и однонаправленные списки на базе массивов указателей на элементы списка
- •2.1.1Представление однонаправленных списков массивами указателей
- •2.1.2Организация файла с однонаправленным списком на базе массивов указателей на элементы списка
- •2.1.3Статические и динамические массивы указателей
- •2.1.4Организация списка дыр
- •2.1.5Функция инициализации файла со списком
- •2.1.6Функция открытия файла со списком
- •2.1.7Функции корректировки указателя на начало списка, заголовка списка дыр, количества элементов списка и текщего размера массива указателей
- •2.1.8Функция выделения записи для нового элемента списка
- •2.1.9Функция освобождения записи удаляемого элемента списка
- •2.1.10 Функция увеличения размера массива указателей на элементы списка
- •2.1.11 Функция вывода содержимого списка
- •2.1.12 Функция поиска элемента списка
- •2.1.13Функция добавления элемента в неотсортированный список
- •2.1.14Функция добавления элемента в отсортированный список
- •2.1.15Функция удаления элемента из отсортированного списка
- •2.1.16Функция удаления списка
- •2.1.17 Функция удаления списка с усечением файла
- •2.1.18 Пример программы обработки отсортированного списка на базе массивов указателей
- •2.1.19Продолжение. Представление однонаправленных списков массивами указателей
- •2.1.20Функция инициализации файла со списком
- •2.1.21Процедура открытия файла со списком
- •2.1.22Процедура корректировки заголовка списка
- •2.1.23 Процедура увеличения размера массива указателей на элементы списка
- •2.1.24Функция поиска элемента списка или позиции вставки нового элемента методом половинного деления
- •2.1.25Функция вывода списка на экран
- •2.1.26Функция добавления нового элемента в отсортированный список
- •2.1.27Функция удаления элемента из отсортированного списка
- •2.1.28Функция удаления списка с усечением файла
- •3Мультисписковые структуры
- •3.1Назначение мультисписков
- •3.2Мультисписки в динамической памяти
- •3.3Мультисписки в бинарных файлах
- •3.4Сохранение и восстановление мультисписка в динамической памяти
- •4Указатели на функции
- •4.1Понятие указателя на функцию и его объявление
- •4.2Переменная-указатель на функцию как параметр другой функции
- •4.3Условия использования указателей на функции
- •4.4Использование указателей на функцию для вызова функций
- •4.4.1Формат переменных-указателей на функцию и бестиповых указателей
- •4.4.2Преобразование переменной-указателя на функцию к бестиповому указателю на функцию
- •4.4.3Вызов функции через бестиповый указатель на функцию
- •4.4.4Преобразование бестипового указателя к переменной типа указатель на функцию
- •4.5Способы вызова функции через указатель на функцию
- •4.5.1Массивы указателей на функции
- •5Совместное использование указателей на данные и указателей на функции
- •5.1Инвариантные функци
- •5.2Функции сравнения
- •5.3Пример 1
- •5.4Пример 2
- •6Оформление курсового проекта
- •6.1Интерфейс программной системы
- •6.2Взаимодействие с мультисписком
- •6.3Отображение содержимого мультисписка
- •6.4Файл справки
- •6.5Документация по курсовому проекту
- •6.5.1Общие положения
- •6.5.2Курсовой проект/работа
- •6.5.3Пояснительная записка
- •6.5.4Техническое задание
- •6.5.5Реферат
- •6.5.6Содержание
- •6.5.7Введение
- •6.5.8 Постановка задачи
- •6.5.9 Анализ решаемой задачи
- •6.5.10Анализ существующих методов организации динамических структур данных
- •6.5.11 Определение путей и методов решения задачи
- •6.5.12Проектирование программы
- •6.5.13Заключение
- •6.5.14Список использованных источников
- •6.6Приложения
1.1.11Продолжение. Динамические массивы нетипизированных указателей
В предыдущих разделах, при реализации динамических массивов указателей на элементы списка, для хранения служебной информации о размере массива и о размере списка используются две специальные переменные - SizeArr и Count соответственно. Это не всегда удобно, особенно если используются несколько списков. Действительно в последнем случае растет количество статических переменных, что усложняет работу с программой. И принципиально это невозможно, если число списков заранее не известно.
Возможным альтернативным решением является хранение служебной информации о массиве указателей в самом массиве. Предпосылками к этому является тот факт, что массив, отводимый под указатели, является динамическим, т.е. он выделяется программе уже во время ее работы. Фактически же выделяется не массив указателей, а соответствующий блок байт. А уже затем указатель на этот блок, по мере необходимости, типизируется (маскируется) соответствующим образом. Но тогда маски (типы) могут быть разными. Т.е. по мере необходимости указатель на один и тот же динамический массив может маскироваться (типизироваться) разными типами данных. В частности, такая техника работы с динамическим массивом позволяет по-разному рассматривать одни и те же его элементы. Например, один и тот же массив в C++ можно рассматривать как массив чисел типа int или массив указателей int*. Или иначе, часть массива можно рассматривать, как массив чисел типа integer, а другую часть массива можно рассматривать как массив указателей типа int*.
В рамках конкретной программы весь выделяемый массив байт рассматривается как состоящий из двух смежных частей. Первая часть массива содержит служебную информацию - два элемента типа int, соответственно общий размер массива и фактическое количество элементов списка. А вторая часть массива используется для хранения указателей на элементы списка.
|
|
индекс |
|
|
M-1 |
|
Незанятая часть массива |
… |
|
|
N+2 |
|
Указатель N -> |
N+1 |
|
|
|
|
Указатель 2 -> |
+3 |
|
Указатель 1 -> |
+2 |
|
Количество элементов в списке N |
+1 |
PtrBase -> |
Размер массива М |
0 |
Рисунок 1.17 – Структура массива указателей, содержащего служебную информацию
Следует подчеркнуть, что приводимое ниже решение основано на том, что элементы служебной информации и указатели имеют один и тот же размер – 4-ре байта. Более общее решение, когда элементы служебной информации и указатели имеют разный размер, рассматривается далее.
Для доступа к первой части массива вводится дополнительный тип для указателя int* – указатель на int.
Тогда, получение размера массива будет выглядеть таким образом:
int SizeAr = ((int*)PtrBase)[0]
Поскольку после операции приведения типа указатель PtrBase воспринимается как указатель на число типа int, то индексация указателя нулем означает задачу – взять значение типа int, записанное по адресу PtrBase.
Для доступа ко второй части массива используется тип для указателя void** – указатель на указатель void*.
Тогда, получение указателя на первый элемент списка будет выглядеть таким образом:
void* PFirstEl = ((void**)PtrBase)[2]
Если посмотреть на рисунок выше, то очевидно, что указатели на элементы списка в массиве расположены начиная с индекса 2, так как первые два элемента заняты служебной нформацией о размере массива и количестве элементов списка.
Для наглядности индексации элементов массива, отведенных для хранения общего размера массива и фактического количества элементов списка, вводятся соответственно переменные PosSize и PosCnt. В действительности это элементы массива с индексами 0 и 1.
Основные типы и переменные имеют следующий вид:
const SizeArrH = 4; /*начальный размер массива указателей*/
const Delta = 3; /*приращение размера массива указателей*/
const PosCnt = 1; /*индекс служебного элемента в массиве,
содержащего количество элементов списка*/
const PosSize = 0; /*индекс служебного элемента в массиве,
содержащего текущий размер массива*/
typedef struct ElmList
{
char Name[30];
int Age;
char Address[30];
} TElmList;
// ---- Объявление переменных ------- //
void* PtrDynArrPtr ; /*указатель на начало массива указателей*/
Все рассмотренные выше функции работы со списками остаются полностью идентичными представленным выше с поправкой на приведенный способ хранения размера массива указателей и фактического количества элементов списка.
Для иллюстрации поправки на приведенный способ хранения размера массива указателей и фактического количества элементов списка ниже приводятся тексты двух служебных функций – расширения массива и текст функции поиска элемента в отсортированном списке. С помощью комментариев в этих процедурах показаны изменения по отношению к функциям, представленным выше:
//------- расширение массива указателей ----- //
void ExpendArrPtr (void* &PtrArPtr)
/*увеличение размера массива указателей*/
{
int SizeW;
void* PtrArPtrW; /*вспомогательный бестиповый указатель на начало
массива указателей*/
SizeW = ((int*)PtrArPtr)[PosSize]; /*сохранение старого размера массива*/
PtrArPtrW = PtrArPtr; /*сохранение указателя на старый массив*/
/*выделение места для нового массива*/
PtrArPtr = new void*[SizeW+Delta+2];
/*копирование старого массива указателей в новый массив*/
for (int J = 2; J <= ((int*)PtrArPtrW)[PosCnt] + 1; J++)
{
((void**) PtrArPtr )[J] = ((void**)PtrArPtrW )[J];
}
/*Перенос значения количества элементов из старого массива в но-
вый*/
((int*)PtrArPtr) [PosCnt] = ((int*)PtrArPtrW)[PosCnt];
((int*)PtrArPtr)[PosSize] = SizeW+Delta+2;/*Запись в новый массив размера
массива, который увеличен на величину Delta, 2 – количество служебных элементов*/
delete [ ] PtrArPtrW; /*удаление старого массива*/
} /*ExpendArrPtr*/
//------------ Быстрый поиск -------------------//
void FindElList2(void* PtrArrPtr, char *Key, int &PosFndEl, bool &FindOK)
/*Поиск выполняется методом половинного деления*/
/*процедура имеет двойное назначение - поиск месторасположения искомого элемента и поиск места вставки добавляемого элемента*/
{
int Middl; /*Middl выступает как средняя граница обрабатываемой части
массива*/
int Hi, Low; /*текущие границы - верхняя, нижняя*/
FindOK = false; /*элемент не найден*/
if (((int*)PtrArrPtr)[PosCnt] == 0)
/*Если список пуст, то позиция вставки нового элемента – начало
массива указателей – индекс 2*/
{
PosFndEl = 2;
return;
}
Low = 2; /*установка начальных значений для верхней и нижней границы
диапазона поиска*/
Hi = ((int*)PtrArrPtr)[PosCnt] + 1;
do
{
/*Вычисление среднего индекса в диапазоне поиска*/
Middl = (Hi - Low) / 2 + Low; /* Middl = (Hi + Low) / 2 */
/*Сравнение ключа и поля Name среднего элемента в диапазоне поиска*/
switch (strcmp(((TElmList*)(((void**)PtrArrPtr)[Middl])) ->Name,Key))
{
case 0: PosFndEl = Middl; /*Элемент найден*/
FindOK = true; /*элемент найден*/
return;
case 1: Hi = Middl - 1; /*Если средний элемент >Key, то граница поиска сужается ниже среднего*/
break;
case -1:Low = Middl + 1; /*Если средний элемент >Key, то грани-
ца поиска сужается ниже среднего*/
break;
}
} while (Low <= Hi); /*Поиск осуществляется до тех пор, пока не будет най
ден элемент или нижняя граница не зашкалит за
верхнюю*/
PosFndEl = Low; /*Перебран весь массив. Искомый элемент не найден.
Определена позиция возможной вставки нового эле-
мента*/
} /*FindElList2*/
//-------- Удаление списка --------//
void DelList (void* &PtrArrPtr)
/*Удаление списка сводится к поочередному удалению всех эле-
ментов списка. По окончанию удаления списка счетчик его элементов
равен 0. Таким образом, удаляются только элементы списка, а массив
указателей на элементы списка сохраняется для возможного после-
дующего наращивания списка. Размер SizeArr массива указателей со-
кращается до начального размера SizeArrH. Для полной очистки па-
мяти от списка вслед за вызовом этой процедуры необходимо уда-
лить массив указателей*/
{
if ( ((int*)PtrArrPtr) [PosCnt] != 0) /*список не пуст*/
{
/*удаление всех элементов внутри списка*/
for (int K = 2; K <= ((int*)PtrArrPtr) [PosCnt] + 1; K++)
{
delete ((void**) PtrArrPtr) [K];
}
/*сокращение размера массива указателей от SizeArr до
SizeArrH*/
/*освобождение места, занимаемого старым массивом*/
delete [] PtrArrPtr;
/*выделение места для нового массива*/
PtrArrPtr = new void*[SizeArrH+2];
((int*)PtrArrPtr)[PosCnt] = 0; /*колич. элементов в новом массиве 0*/
((int*)PtrArrPtr)[PosSize] = SizeArrH; /*установка начального размера
массива в массиве указателей*/
}
} /*DelList*/
