Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Архив WinRAR / 2_Посібник_С_002.doc
Скачиваний:
39
Добавлен:
17.05.2015
Размер:
2.7 Mб
Скачать

11 Динамічне виділення пам'яті

Динамічне виділення пам'яті - спосіб виділення оперативної пам'яті комп'ютера для об'єктів у програмі, при якому виділення пам'яті під об'єкт здійснюється під час виконання програми.

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

Рисунок *.1 – Схематичне зображення «купи».

У міру створення в програмі нових об'єктів, кількість доступної пам'яті зменшується. Звідси випливає необхідність постійно звільняти раніше виділену пам'ять. В ідеальній ситуації програма повинна повністю звільнити всю пам'ять, яка потрібна для роботи. За аналогією з цим, кожна процедура (функція або підпрограма) повинна забезпечити звільнення всієї пам'яті, виділеної під час виконання процедури. Некоректний розподіл пам'яті приводить до т.з. «витоку» ресурсів, коли виділена пам'ять не звільняється. Численні витоки пам'яті можуть призвести до вичерпання всієї оперативної пам'яті і порушити роботу операційної системи.

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

Наприклад нам потрібна пам'ять під масив розміром 6 блоків (Рисунок *.2). У нас вільних всього 8 блоків. Як відомо масив – це послідовний набір однотипних даних, тому нам потрібно послідовно 6 блоків. Як видно з рисунка, ми не можемо виділити цю пам'ять, хоча к-сть блоків для цього є достатньою, тому операція закінчиться невдачею.

Рисунок *.2 – Демонстрація виділення памяті

Для керування динамічним розподілом пам'яті використовується «збирач сміття» - програмний об'єкт, який стежить за виділенням пам'яті і забезпечує її своєчасне звільнення. Збирач сміття також стежить за тим, щоб вільні блоки мали максимальний розмір, і, при необхідності, здійснює дефрагментцію пам'яті (Рисунок*.3).

Рисунок *.3 – Робота дефрагментатора

Для роботи з динамічним виділенням пам’яті використовують функції malloc і calloc, за допомогою яких і відбувається виділення пам’яті, функції realloc, що дозволяє змінювати розмір виділеної ділянки пам’яті. Обовязково з цими ф-ями повинна використовуватись функція free, що звільняє виділену пам'ять.

Функція malloc

Прототип:

void * malloc ( size_t size );

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

Виділений блок пам'яті не ініціалізований, тобто залишається з невизначеними значеннями.

Приклад застосування функції malloc, програма №1:

#include <stdio.h>

#include <stdlib.h>

int main (void)

{

int vari;

char * string;

int num;

printf ("Dovzhina strichky: ");

scanf ("%d", &vari);

string = (char*) malloc (vari+1);

if (string ==NULL)

exit (1);

for (num=0; num <vari; num ++)

string[num]=rand()%vari +'a';

string[vari]='\0';

printf ("Strichka: %s\n",string);

free (string);

system("PAUSE");

return 0;

}

Результат роботи програми:

Ця програма генерує рядок довжиною, яка зазначена користувачем (змінна vari), і заповнює його випадковими символами. Можлива довжина цього рядка обмежена лише кількістю вільної пам'яті в системі, яку mallocможе виділити.

Функція calloc

Прототип:

void * calloc ( size_t num, size_t size );

Виділяє блок пам'яті для масиву з num елементів, кожен з яких має розмір size байт, і ініціалізує всі комірки виділеної пам’яті нулями. В результаті буде виділена пам'ять розміром size*num байт.

У разі успіху повертається вказівник на блок виділеної пам'яті. Цей вказівник завжди має тип void, і може бути приведений до необхідного типу даних при розіменовуванні. Якщо функції не вдалося виділити необхідний блок пам'яті, повертається вказівник на NULL.

Приклад застосування функції calloc, програма №2:

#include <stdio.h>

#include <stdlib.h>

int main (void)

{

int vari, num;

int * p;

printf ("Kilkist' elementiv masyvu: ");

scanf ("%d",&vari);

p = (int*) calloc (vari,sizeof(int));

if (p == NULL)

exit (1);

for (num = 0;num < vari; ++num)

{

printf ("Vvedit' chyslo pid nomerom #%d: ",num);

scanf ("%d",&p[num]);

}

printf ("Vy vvely: ");

for (num = 0;num < vari; ++num)

printf ("%d ",p[num]);

free (p);

system("PAUSE");

return 0;

}

Результат роботи програми:

Слід зазначити що функція callocпотребує для свого виконання більше часу,оскільки проводить ініціалізацію виділеної пам’яті. У випадках, коли є критичною швидкодія програми, доцільно користуватися функцією malloc.

Здійснити заміну функції callocна malloc можна переписавши стрічку :

p = (int*) calloc (vari,sizeof(int));

на

p = (int*) malloc (vari*sizeof(int));

Функція realloc

Прототип:

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

Змінює розмір блоку block, який раніше був виділений за допомогою функційmalloc або calloc. Аргументsizeзадає новий розмір блоку. Вміст блоку не змінюється.

У разі успіху повертається вказівник на блок змінений у розмірі блок пам'яті. Цей вказівник завжди має тип void, і може бути приведений до необхідного типу даних при розіменовуванні. Якщо функції не вдалося змінити розмір блоку, повертається вказівник на NULL.

Приклад застосування функції calloc, програма №2:

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

int *A,i;

if((A=(int*)malloc(10*sizeof(int)))==NULL)

exit(0);

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

A[i]=i;

if((A=(int*)realloc(A,33*sizeof(int)))==NULL)

exit(0);

for(;i<33;++i)

A[i]=i%3;

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

printf("%d ",A[i]);

free(A);

printf("\n");

system("PAUSE");

return 0;

}

Результат роботи програми:

Програма виділяє пам’ять для десяти елементів типу int та заповнює створений масив числами від 0 до 9. Після чого проводиться розширення масиву до розміру 33 елементів типу int. Доповнення масиву заповнюється остачами від ділення на 3 порядкового номеру комірки масиву, при чому вміст перших десяти елементів залишається незмінним.

Функція free

Прототип:

void free ( void * ptr );

Блок пам'яті, раніше виділений за допомогою виклику malloc, calloc буде звільнений, що робить його доступним для подальших дій.

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

В якості параметра передається вказівник на динамічно виділений блок памяті.

Якщо в якості аргументу передається вказівник на NULL, ніяких дій не відбувається.

Значень увагу на те що дана функція нічого не повертає.

Використання функції free показане в попередніх програмах(програми №1 №2 та №3).

Соседние файлы в папке Архив WinRAR