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

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”); // открытие файла в режиме чтения и записи

Основные функции работы с бинарными файлами:

  1. 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);

  1. 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);

  1. 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 );

  1. ftell () – возвращает текущую позицию файлового курсора

Прототип функции:

long int ftel (FILE * f);

  1. feof () – возвращает true если файловый курсор в конце файла и возвращает false в ином случае.

Прототип функции:

int feof (FILE * f);

  1. 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 – Структура массива указателей

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

    • позиционирование курсора файла на байте, соответствующем началу требуемой записи;

    • чтение записи - заданной группы байт;

    • изменение содержимого записи;

    • вновь позиционирование курсора файла на байте, соответствующем началу записи;

    • сохранение записи.

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

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

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