- •Содержание
- •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.14Нетипизированые указатели и память
Выше предложена нестандартная техника работы с массивами с помощью нетипизированных указателей и приведения бестиповых указателей к указателям на определенный тип. Выполнив базирование указателя на некоторый элемент массива, и рассматривая его как элемент некоторого типа с индексом 0, благодаря способу задания диапазона индексов массива от –2N до +2N, можно смещаться относительно этого элемента в область как отрицательных, так и положительных индексов. Меняя шаблоны можно выбирать требуемое содержательное представление об элементах массива. Таким образом, можно перемещаться практически по всей памяти, неся при этом полную ответственность за правильность своих действий.
Однако можно пойти еще дальше и развить эту технику для работы с пространством памяти, не привязываясь к массиву как инструменту структурирования памяти на отдельные элементы и механизмам доступа к этим элементам.
Пусть нетипизированный указатель, каким либо образом базирован в памяти. Например, так
Ptr = &ElmLst;
или так
PtrRec = new TRec;
Ptr = &(*PtrRec);
или так
Ptr = new TRec;
или так
PtrRec = new TRec;
Ptr = PtrRec;
или так
NTPtrList=malloc(sizeof(TRec));
Ptr = &(*((TRec*)NTPtrList));
Далее возможна следующая схема перебазирования нетипизированного указателя:
J = (int) Ptr; /*Значение Ptr*/
Ptr = (void*) (J + Const); /*Перебазирование Ptr*/
или так
Ptr = (void*) (((int)Ptr) + Const); /*Перебазирование Ptr*/
где Const - любое выражение целочисленного типа.
Например, если, заведомо известно, что в памяти – статической или динамической, подряд располагаются несколько элементов списка, то базирование нетипизированного указателя Ptr на последующий элемент списка выполняется следующим образом:
Ptr = (void*) (((int)Ptr) + sizeof(TRec)); /*Перебазирование Ptr на
следующий элемент списка*/
Предложенная схема открывает неограниченные возможности по доступу к оперативной памяти компьютера. Однако следует еще раз подчеркнуть, что программист при этом несет полную ответственность за правильность своих действий.
1.1.15Продолжение. Динамические массивы нетипизированных указателей
В предыдущем – модифицированном варианте представления списка, в начало массива добавлено пространство для хранения максимально ожидаемого размера списка (размер массива) и количества реально существующих элементов списка. Однако указатель на массив указателей по-прежнему ссылается на первый байт такого массива. Это потребовало корректировки текста практически всех функций для учета поправки на место, занятое служебной информацией.
С другой стороны выше показано, что указатель на массив указателей можно сместить на границу между указателями на элементы списка и служебной информацией о размере массива и количестве элементов списка. Такой указатель, назовем его действующий, вновь будет ссылаться на позицию в массиве указателя на элемент списка с индексом 0.
При таком решении тексты функций становятся независимыми от типа и объема служебной информации, содержащейся в массиве. Фактически тексты практически всех процедур возвращается к варианту, когда массив содержал только указатели на элементы списка и не содержал служебной информации. Отличия проявляются только в функциях ExpendArrPtr и DelList, связанных с обработкой полного объема массива указателей.
Таким образом, тексты основных функций возвращаются к состоянию, когда массив указателей содержал только указатели и не содержал служебной информации, а некоторые изменения происходят в исполнительной части программы.
Введем необходимые объявления:
const SizeArrH = 4; //верхняя граница массива указателей
const Delta = 3; /*приращение размера массива указателей*/
const PosCnt = -1; /*индекс служебного элемента в массиве,
содержащего количество элементов списка*/
const PosSize =-2; /*индекс служебного элемента в массиве,
содержащего текущий размер массива*/
typedef struct ElmList
{
char Name[30];
int Age;
char Address[30];
} TElmList;
void* PtrDynArrPtr; /*указатель на позицию указателя на эле-
мент списка с индексом 0 в массиве указателей*/
Тексты основных процедур теперь будут иметь вид:
//------- расширение массива указателей ----- //
void ExpendArrPtr (void* &PtrArPtr)
/*увеличение размера массива указателей*/
/*PtrArPtr содержит адрес указателя на первый элемент списка*/
{
int SizeW;
void* PtrArPtrW; /*вспомогательный бестиповый указатель на начало
массива указателей*/
SizeW = ((int*)PtrArPtr)[PosSize]; /*сохранение старого размера массива*/
PtrArPtrW = PtrArPtr; /*сохранение указателя на старый массив*/
PtrArPtr = new void*[SizeW+Delta+2]; /*выделение места для нового массива*/
/*формирование действующего указателя для нового массива указателей*/
PtrArPtr= (void*)(((int)PtrArPtr) + sizeof(int) * 2);
/*копирование старого массива индексов в новый массив*/
for (int J = 0; J <= ((int*)PtrArPtrW)[PosCnt] - 1; J++)
{
((void**)PtrArPtr)[J] = ((void**)PtrArPtrW)[J];
}
/*копирование реального количества элементов списка из старого
массива указателей в новый массив указателей*/
((int*)PtrArPtr)[PosCnt] = ((int*)PtrArPtrW)[PosCnt];
/*формирование нового размера массива указателей*/
((int*)PtrArPtr)[PosSize] = SizeW + Delta + 2;
/*формирование указателя на начало старого массива указателей*/
PtrArPtrW = (void*)(((int)PtrArPtrW) - sizeof(int) * 2);
delete [ ] PtrArPtrW; /*удаление ста-рого массива*/
} /*ExpendArrPtr*/
//--------- Вывод списка в ListView ------------//
void PrintListInListView(void* PtrArrPtr,System::Windows::Forms::ListView * listView)
{
listView->Items->Clear();
int count = ((int*)PtrArrPtr)[PosCnt];
if (count == 0) return;
for ( int I = 0; I < count; i++ )
{
listView->Items->Add(((TElmList*)(((void**)PtrArrPtr)[i]))->Name);
listView->Items->Item[i]->SubItems->Add(((TElmList*)(((void**)PtrArrPtr)[i]))-> Address);
}
}
//------------ Быстрый поиск -------------------//
void FindElList2(void* PtrArrPtr, char *Key, int &PosFndEl, bool &FindOK)
/*Поиск выполняется методом половинного деления*/
/*процедура имеет двойное назначение - поиск месторасположения
искомого элемента и поиск места вставки добавляемого элемента*/
{
int Middl; /*Middl выступает как средняя граница обрабатываемой части
массива*/
int Hi, Low; /*текущие границы - верхняя, нижняя*/
FindOK = false; /*элемент не найден*/
if (((int*)PtrArrPtr)[PosCnt] == 0)
{
PosFndEl = 0;
return;
}
Low = 0;
Hi = ((int*)PtrArrPtr)[PosCnt] - 1;
do
{
Middl = (Hi - Low) / 2 + Low; /* Middl = (Hi + Low) / 2 */
switch (strcmp(((TElmList*)(((void**)PtrArrPtr)[Middl]))->Name, Key))
{
case 0: PosFndEl = Middl; /*Элемент найден*/
FindOK = true; /*элемент найден*/
return;
case 1: Hi = Middl - 1;
break;
case -1:Low = Middl + 1;
break;
}
} while (Low <= Hi);
PosFndEl = Low; /*Перебран весь массив. Искомый элемент не найден.
Определена позиция возможной вставки нового элемента*/
} /*FindElList2*/
// -------- Добавление в отсортированный список -------//
void AddElSortList(void* PtrArrPtr, void* NewP, int PosAddEl)
{
if (PosAddEl == ((int*)PtrArrPtr)[PosCnt])
{
((void**)PtrArrPtr)[PosAddEl] = NewP;
}
else
{ /*добавляемый элемент располагается в начале или внутри списка*/
for (int K = ((int*)PtrArrPtr)[PosCnt]-1; K >= PosAddEl; K--) /*смещение на
одну позицию вниз*/
{
((void**)PtrArrPtr)[K+1] = ((void**)PtrArrPtr)[K];
}
((void**)PtrArrPtr)[PosAddEl] = NewP;
}
((int*)PtrArrPtr) [PosCnt] = ((int*)PtrArrPtr)[PosCnt] + 1;
return ;
} /*AddElSortList*/
//--------- Удаление элемента -----------//
void DelElList (void* PtrArrPtr, int PosDelEl)
{
if (((int*)PtrArrPtr)[PosCnt] == 0)
{ /*Список пуст*/
return;
}
/*по окончании поиска месторасположения удаляемого элемента в
списке - имеющего позицию PosDelEl}
/*удаление элемента списка*/
delete ((void**)PtrArrPtr)[PosDelEl];
/*возможная корректировка содержимого списка указателей*/
if (PosDelEl != ((int*)PtrArrPtr)[PosCnt] -1)
{
/*удаляемый элемент внутри списка*/
for (int K = PosDelEl + 1; K <= ((int*)PtrArrPtr)[PosCnt] - 1; K++)
{
/*смещение на одну позицию вниз*/
((void**)PtrArrPtr)[K-1] = ((void**)PtrArrPtr)[K];
}
}
((int*)PtrArrPtr) [PosCnt] = ((int*)PtrArrPtr) [PosCnt] - 1;
} /*DelElList*/
//-------- Удаление списка --------//
void DelList (void* &PtrArrPtr)
/*Удаление списка сводится к поочередному удалению всех элемен- тов списка. По окончанию удаления списка счетчик его элементов равен 0. Таким образом, удаляются только элементы списка, а массив указателей на элементы списка сохраняется для возможного после-дующего наращивания списка. Размер SizeArr массива указателей со-кращается до начального размера SizeArrH. Для полной очистки памяти от списка вслед за вызовом этой процедуры необходимо удалить массив указателей*/
{
if (((int*)PtrArrPtr)[PosCnt] != 0) /*список не пуст*/
{
/*удаление всех элементов внутри списка*/
for (int K = 0; K <= ((int*)PtrArrPtr)[PosCnt] - 1; K++)
{
delete ((void**) PtrArrPtr)[K];
}
}
/*сокращение размера массива указателей от SizeArr до Si zeArrH*/
/*освобождение места, занимаемого старым массивом*/
/*позиционирование указателя на старое место*/
PtrArrPtr= (void*) (((int)PtrArrPtr) - sizeof(int)*2);
delete [ ] PtrArrPtr;
/*выделение места для нового полного массива указателей*/
PtrArrPtr = new void* [SizeArrH+2];
/*формирование действующего указателя PtrDnArPtr в новом
массиве указателей*/
PtrArrPtr= (void*) (((int)PtrArrPtr) + sizeof(int) * 2);
/*обнуление счетчика количества элементов списка в новом массиве*/
((int*)PtrArrPtr) [PosCnt] = 0;
/*сохранение начального размера массива в новом массиве*/
((int*)PtrArrPtr) [PosSize] = SizeArrH;
} /*DelList*/
Рассмотрим общую схему работы с таким представлением динамического списка.
Примечание. Данные об элементе списка вводятся в компонент textbox. Свойство Text данного компонента содержит строку ввода и имеет тип System::String*, а строковое поле элемента списка объявлено как массив символов (например, char [30]). Для перевода значения типа System::String* в char* воспользуемся дополнительной функцией char* SystemStringToChar(System::String * string)
char* SystemStringToChar(System::String * string)
{
return (char*) (void*) System:: Runtime:: InteropServices:: Marshal:: StringToHGlobalAnsi(string);
}
Кнопка – создание списка. Массив динамический, создание списка состоит в выделении в динамической памяти участка для массива указателей и двух служебных элементов, которые будут содержать значения количества элементов и размер массива и инициализации этих служебных элементов.
// ------ Создать список -------------//
void Form1::btn_Create_Click(System::Object * sender, System::EventArgs* e)
{
/*выделение места под изначально ожидаемый максимальный размер списка плюс место для служебной информации*/
PtrDynArrPtr = new void*[SizeArrH+2];
//формирование действующего указателя PtrDnArPtr в новом массиве указателей
PtrDynArrPtr= (void*)(((int)PtrDynArrPtr) + sizeof(int) * 2);
/*обнуление счетчика количества элементов списка*/
((int*)PtrDynArrPtr)[PosCnt] = 0;
/*сохранение начального размера массива*/
((int*)PtrDynArrPtr)[PosSize] = SizeArrH;
}
Занесение нового элемента списка Осуществляется по нажатию кнопки «Добавить»на главной форме. Для ввода полей нового элемента создана дополнительная форма FAdd, которая содержит компоненты textBox для ввода и кнопку «+» для подтверждения окончания ввода информации о новом элементе.
Рисунок 1.19 - Дополнительная форма для ввода информации о новом элементе.
Для кнопки "+" на дополнительной форме необходимо установить свойству DialogResult значение OK для того чтобы по окончании ввода информации можно было проанализировать – чем закончился диалог с пользователем – нажатием кнопки «+» или нжатием кнопки Выход.
Рисунок 1.20 - Установка свойства DialogResult для кнопки "+"
// ------------ Добавить новый элемент ------------ //
void Form1::btnAdd_Click(System::Object * sender, System::EventArgs * e)
{
FormAdd *FAdd = new FormAdd(); /*создание дополнительной формы*/
/*Вызов дополнительной формы*/
/*Если на дополнительной форме бйла нажата кнопка Добавить,
то DialogResult формы будет иметь значение OK */
if (FAdd->ShowDialog() == DialogResult::OK)
{
/*Выделяем память для нового элемента списка*/
void* NPtrElm =new TElmList;
/*Заполняем поля нового элемента из введенной на до
полнительной форме информации в компонентах textBox*/
((TElmList*)NPtrElm) ->Age = Convert::ToInt32(FAdd->textBoxAge->Text);
strcpy(((TElmList*)NPtrElm)-> ame, SystemStringToChar(FAdd->
textBoxName->Text));
strcpy(((TElmList*)NPtrElm)->Address, SystemStringToChar(FAdd-> textBoxAdres->Text));
int PosAdd;
bool FindOK;
/*Поиск позиции вставки нового элемента в массиве*/
FindElList2 (PtrDynArrPtr, ((TElmList*)NPtrElm) ->Name,
PosAdd, FindOK);
/*Если массив заполнен полностью, то расширение массива*/
if (((int*)PtrDynArrPtr)[PosSize] == ((int*)PtrDynArrPtr)[PosCnt])
{
ExpendArrPtr (PtrDynArrPtr);
}
/*Добавление нового элемента в список в позицию PosAdd*/
AddElSortList (PtrDynArrPtr, NPtrElm, PosAdd );
/*Вывод списка в listBox*/
PrintListInListView(PtrDynArrPtr,listView1);
}
}
/*Поиск элемента по полю Name*/
void Form1::btnFindEl_Click(System::Object * sender, System::EventArgs * e)
{
int PosFndEl; /*позиция в массиве найденного элемента*/
bool FindOK; /*Результат поиска - true -найден,false-нет такого эле-
мента*/
char *Key = SystemStringToChar(textBoxFind->Text); /*Ключ поиска берем
из компонента textBoxFind на главной форме*/
/*Вызов процедуры поиска элементов*/
FindElList2(PtrDynArrPtr, Key, PosFndEl, FindOK);
if (FindOK)
{
char* findResult = SystemStringToChar(String::Concat(textBoxFind->
Text, S", адрес: ", ((TElmList*)(((void**)PtrDynArrPtr)[PosFndEl]))->
Address));
MessageBox(NULL, findResult, "Результат поиска", MB_OK);
}
else
{
MessageBox(NULL,"Элемент не найден","Результат поиска",MB_OK);
}
}
/*Удаление элемента списка по полю Name*/
void Form1::btnDel_Click(System::Object * sender, System::EventArgs * e)
{
//Ключ удаляемого элемента вводится в компонент textBoxDel на главной форме
char * Key=SystemStringToChar(textBoxDel->Text);
int PosDel;
bool FindOK;
/*Поиск удаляемого элемента*/
FindElList2(PtrDynArrPtr,Key,PosDel,FindOK);
if (FindOK == false) /*Если элмент не найден*/
{ return; }
DelElList (PtrDynArrPtr, PosDel);
PrintListInListView(PtrDynArrPtr, listView1);
}
/*---------- Удаление всего списка ---------*/
void Form1::btnDelList_Click(System::Object * sender, System::EventArgs * e)
{
DelList (PtrDynArrPtr);
PrintListInListView(PtrDynArrPtr, listView1);
/*Позиционирование указателя на начало массива*/
PtrDynArrPtr = (void*)(((int)PtrDynArrPtr) - sizeof(int) * 2);
/*Освбождение памяти от массива полностью*/
delete [ ] PtrDynArrPtr;
}
Продолжение. Динамические массивы нетипизированных указателей. Альтернативное решение
В предыдущем – модифицированном варианте представления списка, в начало массива добавлено пространство для хранения максимально ожидаемого размера списка (размер массива) и количества реально существующих элементов списка.
С другой стороны выше показано, как указатель на массив указателей можно сместить на границу между указателями на элементы списка и служебной информацией о размере массива и количестве элементов списка. Такой указатель вновь ссылается на позицию в массиве указателя на элемент списка с индексом 0.
При таком решении тексты функций становятся независимыми от типа и объема служебной информации, содержащейся в массиве.
Другим решением той же задачи, когда тексты функций становятся независимыми от типа и объема служебной информации является использование структуры, содержащей три поля: размер массива указателей Size, реальное количество указателей в массиве Count и нетипизированный указатель на массив указателей PtrArrPtr.
typedef struct RecwithPtrAr
{
int Size;
int Count;
void* PtrDnArrPtr;
} TRecWithPtrArrPtr;
TRecWithPtrArrPtr RecWithPtrArrPtr;
TRecWithPtrArrPtr * PtrRecWithPtrArrPtr;
Такой подход естественно потребует корректировки текстов функций по работе со списком, однако эти корректировки носят чисто синтаксический характер, учитывающий выбранную структуру данных.
В основном корректировка определяется тем, что в функции вместо указателя на динамический массив указателей будет передаваться переменная типа TRecWithPtrArrPtr или указатель типа TPtrRecWithPtrArrPtr.
