Організація довільного доступу до елементів двійкових файлів 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Організація довільного доступу до елементів двійкових файлів



Організація довільного доступу до компонентів файлу дозволяє зчитувати значення з будь-якої позиції в файлі, а також записувати нову інформацію в будь-яке місце у файлі. Але до файлів з довільним доступом пред'являється одна жорстка вимога: їх компоненти повинні мати однакову довжину. Двійкові файли дозволяють забезпечити задоволення цієї вимоги. Про те, щоб дані, які будуть знаходитися у файлі довільного доступу, мали однаковий розмір, слід подбати у момент створення файла.

Ще раз нагадаємо про те, що первинна запис у файл можлива тільки в режимі послідовного доступу.

Для організації довільного доступу до елементів файлу можна здійснити за допомогою функції fseek (), прототип якої описаний в заголовному файлі stdio.h. Синтаксичне опис функції:

int fseek (FILE * stream, long offset, int whence);

Функція fseek () переміщає внутрішній покажчик файлового потоку, змінюючи місце у файлі, з якого починається наступна операція читання або запису. У разі успішного завершення функція повертає 0, в разі помилки-ненульове значення.

Параметри функції:

FILE * stream - yказатель на відкритий файловий потік, аналогічної повертається функцією fopen ();

long offset - число байтів, на яке потрібно перемістити файловий покажчик у напрямку, вказаному параметром whence. Для переміщення файлового покажчика у зворотному напрямку (в бік початку файлу), слід встановлювати отто рівним від'ємному значенню;

int whence - вказує положення точки відліку файлового покажчика, від якої відбуватиметься його переміщення. Значення аргументу whence представлені нижче.

Значення аргументу whence

Значення Опис

SEEK_SET Переміщення файлового покажчика відбувається відносно початку файлу

SEEK_END Переміщення файлового покажчика відбувається відносно кінця файлу

SEEK_CUR Переміщення файлового покажчика відбувається відносно поточної позиції файлового покажчика

При використанні функції fseek () слід дотримуватися обережності, так як через обмеження DOS потрапляння за межі файлу найчастіше не приводить до генерації помилки, тому програмісту самому слід вживати заходів для запобігання звернення до диска за межами відомих кордонів файлу.

При організації довільного доступу використовується функція ftell (), що здійснює навігацію всередині файлу. Прототип функції описаний в stdio.h. Ця функція повертає внутрішній покажчик файлового потоку, рівний зсуву в байтах від початку двійкового файлу до байта, з якого починається наступна операція введення / виводу. Це значення можна передати функції fseek (), або використовувати будь-яким іншим чином.

Синтаксичне опис функції:

long int ftell (FILE * stream).

дінственним параметром функції є покажчик на відкритий файловий потік.

Наступні оператори демонструють можливості функції fseek ():

fseek (f, sizeof (t), SEEK_CUR); - переміщує файловий вказівник з поточної позиції на наступну;

fseek (f,-sizeof (t), SEEK_CUR); - на попередню позицію;

fseek (f, 0, SEEK_END); - на кінець файлу.

При роботі з двійковими файлами не слід забувати додавати букву «b» при вказівці режиму доступу функції fopen (), але потрібно бути особливо уважним, якщо передбачається, що буде здійснюватися довільний доступ до компонентів даного файлу.

Приклад. Записати нуль на місце мінімального значення у файлі Test.dat.

# Include <stdio.h>

# Include <stdlib.h>

# Include <io.h>

int main ()

{FILE * f_test;

int i_min; / / номер мінімального елемента

int handle; / / дескриптор файлу

int value, min;

f_test = fopen ("Test.dat", "r + b");

if (! F_test) {puts ("Не можна відкрити файл! \ n");

exit (1);

}

handle = fileno (f_test); / / перетворити відкритий файловий

/ / Потік в дескриптор файлу

fread (& min, sizeof (int), 1, f_test);

i_min = 0;

for (int i = 0; i <filelength (handle); i + +)

{Fread (& value, sizeof (int), 1, f_test);

if (value <min) {min = value;

i_min = i;

}

}

fseek (f_test, i_min * sizeof (int), SEEK_SET);

fwrite (& zero, sizeof (int), 1, f_test);

fclose (f_test);

return 0;

}

Функція filelength (f_inp) в якості аргументу приймає дескриптор файлу, відкритого функцією fopen (), і повертає розмір цього файлу. Для отримання дескриптора, використовуваного для ідентифікації файлу, служить функція перетворення файлового відтоку в дескриптор fileno (). Аргументом функції fileno (FILE * stream) є файловий потік. Негативне значення дескриптора служить ознакою помилки.

Зауважимо, що, як і в масивах, перший елемент, що знаходиться у файлі, має номер 0, другий - 1 і т.д.

Для визначення кінця файлового потоку використовується функція feof (), прототип якої описаний в заголовному файлі stdio.h. Функція повертає істину (1), якщо внутрішній покажчик заданого файлового потоку знаходиться за останнім байтом файлу і брехня (0), якщо внутрішній покажчик файлу знаходиться не в кінці файлу.

Питання для контролю:

1.Як здійснюється обробка двійкових файлів?

2. Як здійснюється послідовний доступ до елементів двійкових файлів?

3.Як здійснюється організація довільного доступу до елементів двійкових файлів?

 

Література:

1. Т.Сван, “ Освоение BORLAND C++ 4.5 Практический курс”, Киев, “Диалектика”, 1996 г.- с. 270-272

Урок №62

(згідно робочої навчальної програми)

Тема:Динамічні структури даних

Питання:

1.Робота динамічних структур даних.

2.Функції управління пам'яттю

Робота динамічних структур даних.

Пам'ять для зберігання даних може виділятися як статично, так і динамічно. У першому випадку виділення пам'яті виконує компілятор, що зустрів при компіляції оголошення об'єкта. Відповідно до типу зустрітого об'єкта обчислюється обсяг пам'яті, необхідний для його розміщення. Клас пам'яті задає місце, де ці об'єкти (дані) будуть розташовуватися. Це може бути сегмент даних або стек. Нагадаємо, що стек (магазин, список LIFO - Last In First Out) являє собою послідовний список змінної довжини, в якому включення і виключення елементів проводиться тільки з одного боку. Головні операції при роботі зі стеком - включення і виключення елемента - здійснюються з вершини стека, причому в кожен момент доступний елемент, що знаходиться на вершині стека.

Часто виникають ситуації, коли заздалегідь не відомо, скільки об'єктів - чисел, рядків тексту та інших даних буде зберігати програма. У цьому випадку використовується динамічне виділення пам'яті, коли пам'ять займається і звільняється в процесі виконання програми. При використанні динамічної пам'яті (ДП) відпадає необхідність заздалегідь розподіляти пам'ять для зберігання даних, що використовуються програмою. Управління динамічної пам'яттю - це здатність визначати розмір об'єкта і виділяти для його зберігання відповідну область пам'яті в процесі виконання програми.

При динамічному виділенні пам'яті для зберігання даних використовується спеціальна область пам'яті, так звана «купа» (heap). Обсяг «купи» і її місце розташування залежать від моделі пам'яті, яка визначає логічну структуру пам'яті програми.

При кожному зверненні до функції розподілу пам'яті виділяється запитане число байт. Адреса початку виділеної пам'яті повертається в точку виклику функції і записується в змінну-покажчик. Створена таким чином змінна називається динамічної змінної. Розподілена пам'ять гарантується від повторного виділення при наступних зверненнях за байтами пам'яті. Подальша робота з виділеною областю здійснюється через змінну-покажчик, що зберігає адресу виділеної області пам'яті. Сама ж змінна залишається безіменною.

Якщо виділена ділянка пам'яті більше не потрібно, він може бути звільнений. При високій активності по динамічному розподілу пам'яті «купа» фрагментируется. Для пом'якшення негативних наслідків фрагментації служать функції повторного розподілу пам'яті. Вони намагаються або розширити, або зменшити розмір раніше виділеного блоку пам'яті.

Функції управління пам'яттю

Розглянемо функції управління пам'яттю:

- Malloc () - призначена для виділення безперервній області пам'яті заданого розміру, наприклад, void * malloc (size_t size),

де size_t - тип результату операції sizeof, який визначається в різних об'єктах-заголовках (stdio.h, stdlib.h, string.h та ін) і відповідний типу unsigned long; size - розмір виділюваної пам'яті в байтах.

Функція malloc повертає покажчик без типу. Якщо виділити пам'ять не вдалося, функція повертає значення NULL.

При використанні в програмах на C + + потрібно виконувати явне перетворення типу покажчика. Якщо необхідно створити символьну Рядок у динамічній пам'яті, то спочатку треба оголосити покажчик char * S1, а потім виділити область під символьну рядок за допомогою функції

malloc ():

S1 = (char *) malloc (V);

де V - вираз, значенням якого є цілі невід'ємні числа, наприклад, V = 10, V = 10 +7 і т. д.

Далі покажчик S1 використовується у функціях для роботи з рядками так само, як якщо б рядок була оголошена як масив символів або покажчик на статичну рядок, наприклад:

gets (S1);

scanf ("% s", S1);

printf ("% s", S1);

int n = strlen (S1);

- Calloc () - призначена для виділення пам'яті під задану кількість об'єктів, кожен з яких має розмір size байт, всього n 'size байт. Повертає покажчик на перший байт виділеної область пам'яті.

Якщо виділити пам'ять не вдалося, функція повертає значення NULL:

void * calloc (size_t n, size_t size);

- Realloc () - призначена для зміни розміру динамічно виділеної області, адресується покажчиком ptr, при цьому розмір області може як збільшуватися, так і зменшуватися:

void * realloc (void * ptr, size_t size);

де ptr - вказівник на первісну область пам'яті, size - новий розмір області пам'яті.

Функція повертає адресу нової області пам'яті, при цьому стара область звільняється. Дані з визволеної області копіюються в нову, але не більше size байт. У деяких випадках область, адресується спочатку покажчиком ptr, може зберегтися, тільки зміниться її розмір. Якщо виділити пам'ять не вдалося, функція повертає значення NULL.

Для того щоб уникнути помилок у зв'язку з переміщенням даних в динамічній пам'яті, функцію realloc () слід використовувати так:

ptr2 = realloc (ptr1, new_size);

if (ptr2! = NULL) / * Перевірка виділення нової пам'яті * /

ptr1 = ptr2; / * Запис адреси нової області пам'яті в вихідний покажчик * /

Таким чином, покажчик ptr1 завжди буде зберігати поточний адресу розміщення даних, незалежно від того, були дані переміщені функцією realloc () чи ні.

- Free () - призначена для звільнення пам'яті, виділеної функціями malloc (), calloc (), realloc (). Після виконання функції звільнена пам'ять може бути виділена знову під інші дані:

void free (void * ptr);

де prt - покажчик на область пам'яті, створеної тільки функціями динамічного управління пам'яттю malloc (), calloc (), realloc (). Використання інших покажчиків у функції free робить її поведінку невизначеним і може призвести до втрати керування пам'яттю. Розглянемо приклад створення динамічного числового масиву. Функція calloc () виділяє пам'ять для 10 елементів типу int. Покажчик ptr зберігає адресу першого елемента масиву. Значення елементів вводяться з клавіатури. Потім обчислюється сума всіх елементів. Після виведення результату на екран пам'ять, розподілена під масив, звільняється:

# Include <stdlib.h>

# Include <stdio.h>

int main (void)

{

int * ptr = (int *) calloc (10, sizeof (int));

int s = 0;

for (int i = 0; i <10; i + +)

scanf ("% d", & ptr [i]);

for (i = 0; i <10; i + +)

s + = ptr [i];

printf ("Summa =% d \ n", s);

free (ptr);

return 0;

}

Після звільнення пам'яті значення адреси, присвоєне покажчику ptr, не слід використовувати, так як ця галузь може бути виділена під іншу змінну. Рекомендується в такий покажчик записати значення порожнього покажчика NULL (в VC + +6.0 це обов'язково):

ptr = NULL;

Наведемо приклад програми, що використовує динамічну рядок. Дан статичний масив рядків. Відсортувати масив по зростанню, використовувати в якості допоміжної змінної (буфера) динамічну змінну:

# Include <stdio.h>

# Include <conio.h>

# Include <alloc.h>

# Include <string.h>

int main ()

{

char * S1; / * Змінна-покажчик * /

/ * Статичний масив рядків * /

static char list [3] [15] = {"Ivanov", "Sidorov", "Petrov"};

int i, j;

/ * Виділення пам'яті для буфера в динамічній області * /

S1 = (char *) malloc (15);

for (i = 0; i <2; i + +)

{

for (j = 0; j <2-i; j + +)

{

if (strcmp (list [j], list [j +1])> 0)

{

strcpy (S1, list [j]);

strcpy (list [j], list [j +1]);

strcpy (list [j +1], S1);

}}}

for (i = 0; i <3; puts (list [i]), i + +);

getch ();

free (S1); / * Звільнення пам'яті * /

S1 = NULL;

return 0;

}

На закінчення відзначимо, що в програмах, що використовують мову C + +, рекомендується для управління динамічною пам'яттю використовувати кошти саме цієї мови - оператори new і delete, які будуть розглянуті пізніше.

Питання для контролю:

1.Яка різниця між статичним та динамічним розподілом в пам'яті?

2. Які є функції керування пам’яттю?

3. Що робить функція malloc ()?

Література:

1.Шилдт Г.: Пер. с англ. –М.: Издательский дом “Вильямс”, 2002. –704 с.- с. 386-391

Урок №63

(згідно робочої навчальної програми)



Поделиться:


Последнее изменение этой страницы: 2016-08-10; просмотров: 325; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 54.156.48.192 (0.053 с.)