- •Содержание
- •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Приложения
2.1.2Организация файла с однонаправленным списком на базе массивов указателей на элементы списка
Статические массивы указателей на элементы списка располагаются в фиксированном месте файла.
В случае использования динамических массивов указателей такая возможность является лишь частным случаем, так как в связи с ростом размера списка меняется и размер массива, а это требует нового объема для его хранения. Поэтому, в общем случае, в пределах файла выделяется фиксированная область (запись) – указатель на массив указателей на элементы списка, содержащая местоположение первого байта первого элемента массива.
Для представления однонаправленных списков в последующих примерах будет использоваться следующее объявление списка:
typedef struct ElmList
{
char Name[30];
int Age;
char Address[30];
} TElmList;
typedef int TElArPtrFile; /*тип элемента массива указателей*/
typedef FILE* TFile;
Массив указателей на элементы списка можно рассматривать как набор элементов типа TElmArrPtrFile. При инициализации списка все элементы этого массива должны получить начальное значение "элемент отсутствует".
Зарезервированный объем массива указателей на элементы списка может превышать реальное количество элементов списка. Поэтому необходимо хранить текущий размер массива указателей и реальное количество элементов списка.
Обработка списка производится в режиме файла, открытого для чтения. Поэтому стандартным способом выделения памяти для нового элемента списка является добавление новой записи в конец файла.
С другой стороны в процессе использования списка часть его элементов может быть удалена. В результате внутри файла образуются "дыры". Естественным было бы поступать так же, как уже рассматривалось выше - при необходимости выделения памяти для нового элемента списка использовать эти дыры и, только в случае, если все дыры были использованы, добавлять новую запись к файлу. Для чего дыры объединяются в единый список и выделяется фиксированную область (запись) файла – заголовок списка дыр, содержащая местоположение первого байта первого элемента списка дыр.
Для представления в файле заголовков списка дыр, указателя на массив указателей на элементы списка, текущего размера массива указателей на элементы списка и реального количества элементов списка будут использоваться структуры, отличные от структур, предназначенных для элементов списка.
Блок из первых 4-х байт - условно запись №0, зарезервирован под заголовок списка дыр. Второй блок из 4-х байт - условно запись №1, зарезервирован под указатель на массив указателей на элементы списка. Третий блок из 4-х байт - условно запись №2, зарезервирован под текущий размер массива указателей на элементы списка. Четвертый блок из 4-х байт - условно запись №3, зарезервирован под счетчик реального количества элементов списка.
Примем, что размер массива указателей N задается количеством указателей, которые могут быть размещены в этом массиве.
Для записей - элементов списка, в приводимом примере используются блоки из 68 байт. Месторасположение этих записей определяется типом и размером используемого массива указателей – статический или динамический.
Так как размеры обрабатываемых записей разные – 4-х и 68-ми байт, то считывание и запись в файл будет производиться блоками разных размеров, или же размер блка будет составлять 1-н байт, а количество считываемых или записываемых блоков будет зависеть от типа считываемой информации.
Таким образом, функция открытия файла для чтения и записи будет иметь вид:
FILE * f ; // объявление файлового указателя
f = fopen(“имя файла”, “режим открытия”); // открытие файла
Таблица 2.1- режимы открытия бинарного файла:
Значение |
Предназначение файла |
Rb |
Открыть бинарный файл для чтения |
Wb |
Создать бинарный файл для записи |
Ab |
Добавить записи в конец бинарного файла |
r+b |
Открыть бинарный файл для чтения и записи |
w+b |
Создать бинарный файл для чтения и записи |
a+b |
Добавить записи в конец бинарного файла или создать бинарный файл для чтения и записи |
Пример. Открытие бинарного файла для считывания и записи:
FILE * f ; // объявление файлового указателя
f = fopen(“File1.ttt”, “r+b”); // открытие файла в режиме чтения и записи
Основные функции работы с бинарными файлами:
fread () – считывание блоков данных любого типа из файла
Прототип функции:
size_t fread (void * buf, size_t size, size_t count, FILE * f)
Функция возвращает количество считанных блоков.
Примечание. Тип size_t определен как целое число без знака. Где:
buf - указатель на область памяти, в которую записываются данные, считанные из файла;
count – количество блоков, подлежащее считыванию;
size – длина 1-го блока (размер блока в байтах);
f – файловый указатель.
Пример. Считывание элемента списка типа TElmList из файла:
TElmList ElList;
fread (&ElList, sizeof(TElmList), 1, f);
fwrite () – запись блоков данных любого типа в файл
Прототип функции:
size_t fread (void * buf, size_t size, size_t count, FILE * f)
Функция возвращает количество записанных блоков.
Примечание. Тип size_t определен как целое число без знака. Где:
buf - указатель на область памяти, содержащую данные, которые записываются в файл;
count – количество блоков, подлежащее записи;
size – длина 1-го блока (размер блока в байтах);
f – файловый указатель.
Пример. Запись элемента списка типа TElmList в файл:
TElmList ElList;
fwrite (&ElList, sizeof(TElmList), 1, f);
fseek () – установка курсора файла в заданную позицию
Прототип функции:
int fseek (FILE * f, long int смещение, int точка отсчета)
Функция возвращает 0 если выполнена успешно, а если возникла ошибка – возвращается ненулевое значение.
Курсор файла премещается от точки, заданной параметром точка отсчета на количество байт, заданоого параметром смещение.
Параметр точка отсчета задается одним из макросов:
Таблица 2.2 Возможные значение параметра “точка отсчета” для использования в функции fseek
Точка отсчета |
Имя макроса |
Начало файла |
SEEK_SET |
Текущая позиция |
SEEK_CUR |
Конец файла |
SEEK_END |
Пример. Пусть необходимо переместить курсор файла, находящийся в начале файла на 4 байта. Для этого необходимо вызвать функцию fseek таким образом :
fseek ( f, 4, SEEK_SET );
ftell () – возвращает текущую позицию файлового курсора
Прототип функции:
long int ftel (FILE * f);
feof () – возвращает true если файловый курсор в конце файла и возвращает false в ином случае.
Прототип функции:
int feof (FILE * f);
rewind () – устанавливает курсор в начало файла.
Прототип функции:
void rewind (FILE * f);
В случае статических массивов указателей элементы массива указателей на элементы списка располагаются, начиная с байта №16, и занимают блок байт размером N*4, где N – максимально возможное количество элементов списка. Таким образом, элементы списка располагаются, начиная с байта с номером 16+N*4.
В случае динамических массивов указателей место для элементов массива первоначально также отводится, начиная с байта №16, а сами элементы массива занимают блок байт размером N*4, где N – первоначальный размер массива. В результате элементы списка располагаются, начиная с байта с номером 16+N*4. Однако в последующем, при необходимости увеличения размера массива указателей, место для нового объема массива отводится уже в конце файла и выполняется корректировка указателя на массив указателей на элементы списка.
Примечание. Следует отметить, что необходимость увеличения размера массива указателей в случае использования динамических массивов указателей приводит к образованию дыры размером в объем этого массива, а при неоднократном увеличении размера массива указателей образуется совокупность таких дыр. Эффективное использование освобождающегося пространства требует изменения подхода к организации и использованию списка всех имеющихся в файле дыр, о чем будет сказано ниже.
Таким образом, обобщенная структура файла в случае использования статических массивов указателей, а также на начальном этапе в случае использования динамических массивов указателей - до момента увеличения их размеров и, соответственно, изменения местоположения, имеет вид:
№ бт |
0 3 |
4 7 |
8 11 |
12 15 |
16 16+N*4-1 |
… |
M |
|
Заголовок списка "дыр" |
Указатель на массив указателей на элементы списка |
Размер N массива указателей на элементы списка |
Количество K элементов списка |
Массив указателей на элементы списка |
… |
|
Рисунок 2.2 – Структура файла со списком на базе массива указателей до момента увеличения размера массива
Такая структура файла требует предварительной инициализации файла пред его использованием для представления списка.
При очередном обращении к списку необходимо выполнить открытие файла со списком, а после завершения обработки списка – выполнить закрытие файла.
Обобщенная структура массива указателей имеет вид – Рисунок 2.3:
|
|
Индекс |
|
|
N-1 |
|
Незанятая часть массива |
… |
|
|
K |
|
Указатель K -> |
K-1 |
|
|
|
|
Указатель 2 -> |
1 |
PtrBase -> |
Указатель 1 -> |
0 |
Рисунок 2.3 – Структура массива указателей
Следует особо обратить внимание на следующую особенность обработки элемента списка в файле. Она определяется тем, что указатели на элементы списка и сами элементы списка располагаются в файле, а их обработка выполняется в оперативной памяти. При этом следует учитывать особенности поведения курсора файла. После очередной операции чтения или записи этот курсор смещается на требуемое количество байт. Поэтому задача, например, изменения значения какого-либо поля элемента списка сводится к выполнению следующей цепочки действий:
позиционирование курсора файла на байте, соответствующем началу требуемой записи;
чтение записи - заданной группы байт;
изменение содержимого записи;
вновь позиционирование курсора файла на байте, соответствующем началу записи;
сохранение записи.
Ниже приводится программа работы со списками на базе статических массивов указателей, позволяющая добавлять новые и удалять существующие элементы организованного таким образом списка. Допускает добавление и удаление дубликатов - элементов списка, имеющих одинаковые ключи.
Далее будут рассмотрены особенности такой работы со списками на базе динамических массивов указателей.
Приведенные в рассматриваемых программах механизмы могут быть легко обобщены для хранения в одном файле нескольких списков. Для этого необходимо задать соответствующее количество пар - заголовок списка дыр и указатель на массив указателей на элементы списка. Соответственно необходимо предусмотреть соответствующие наборы функций для работы с такими списками.
