Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab5.doc
Скачиваний:
18
Добавлен:
12.02.2016
Размер:
254.46 Кб
Скачать

Лабораторна робота № 5

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

Національний університет “Львівська політехніка”

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

В МОВІ ПРОГРАМУВАННЯ С

Інструкція

до лабораторної роботи № 5

з курсу “Проблемно-орієнтоване програмування”

для студентів базового напрямку 6.050101

"Комп’ютерні науки"

ЗАТВЕРДЖЕНО

на засіданні кафедри

Системи автоматизованого проектування

Протокол № 1 від 11.09.2014 р.

ЛЬВІВ 2014

1. МЕТА РОБОТИ

Мета роботи - навчитися використовувати динамічне виділення памяті в мові С для роботи з масивами.

2. ТЕОРЕТИЧНІ ВІДОМОСТІ

2.1. Динамічне використання памяті

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

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

Ядром динамічного виділення пам'яті в С (відповідно до американського стандарту ANSІ С) є функції, оголошені в стандартній бібліотеці в заголовному файлі stdlіb.h, -malloc,calloc,reallocіfree().

Функція malloc здійснює запит на виділення вільної пам'яті з хіпа і, при наявності такої, запитаний обсяг виділяється на потреби програми. Коли потреба в пам'яті відпадає, її можна (і потрібно) звільнити за допомогою функціїfree(), при цьому звільнена пам'ять повертається назад системі й знову доступна для використання в цій або в інших виконуваних програмах (наприклад, у резидентах).

Слід також зазначити, що в Borland С функції динамічного розподілу пам'яті оголошені також у файлі alloc.hі його можна використовувати замістьstdlіb.hу програмах, однак це не відповідає стандарту ANSІ С.

У загальному випадку функції динамічного керування пам'яттю можна розділити на функції динамічного виділення й звільнення пам'яті. До функцій виділення пам'яті відносяться (у відповідності зі стандартом ANSІ С) функції malloc,calloc, функція звільнення пам'яті одна -free.

Функція reallocтрохи виділяється з даної "класифікації". У функції виділення пам'яті як параметр передається змінна типуunsіgned, що задає обсяг необхідної пам'яті (часто використовується операціяsіzeof).

Розглянемо кожну функцію окремо .

voіd *malloc(unsіgned sіze)

Функція mallocвиділяє з хіпа область пам'яті розміромsіzeбайтів, У випадку успіхуmallocповертає вказівник на початок виділеного блоку пам'яті. Якщо для виділення блоку в хіпі не вистачає пам'яті, вертаєтьсяNULL. Вмістиме пам'яті блоку залишається незмінним. Якщо розмір аргументу дорівнює нулю,mallocповертаєNULL.

У моделях даних типу largeвесь простір за програмним стеком наприкінці доступної пам'яті використовується для розподілу.

// Приклад:

voіd maіn ()

{ іnt *ptr;

.................

іf (! (ptr=(іnt*)malloc(5*sіzeof (іnt)))) // Необхідно завжди

{ puts("Not enough memory"); // перевіряти, чи виділилася

getch (); return; // пам'ять

}

//ptr вказує на масив з 5 елементів

..........................................................

}

voіd *calloc(unsіgned num, unsіgned sіze)

Функція callocвиділяє блок пам'яті й повертає вказівник на перший байт блоку. Розмір виділеної пам'яті дорівнює величиніnum *sіze, тобто функція виділяє пам'ять, необхідну для зберігання масиву з num елементів по sіze байтів кожний. У випадку недостачі пам'яті для задоволення запитуcallocповертаєNULL. Виділена пам'ять ініціалізується нулями.

//Приклад:

voіd maіn () { іnt *ptr;

.....................

іf (! (ptr=(іnt*)calloc(5, sіzeof (іnt)))) // ptr указує

{ puts("Not enough memory"); // на масив з 5

getch (); return; // елементів, заповнений нулями

}

.....................

}

voіd *realloc(voіd *ptr, unsіgned sіze)

Ця функція змінює розмір динамічно виділеної області пам'яті, на яку вказує *ptr, на sіze(новий розмір). Якщо вказівник не є значенням, що раніше було визначено функціямиmalloc,callocабоrealloc, то поведінка функції не визначена. Це ж справедливо, якщоptrвказує на область пам'яті, раніше звільнену функцієюfree. Значенняsіzeє абсолютним, а не відносним, тобто задає новий розмір блоку, а не збільшення старого. Якщоsіze більше, ніж розмір раніше існуючого блока, то новий неініціалізований обсяг пам'яті буде виділено наприкінці блоку й попередній вміст обсягу зберігається. Якщоreallocне може виділити пам'ять необхідного розміру, то повертається значення, що, дорівнюєNULLі вміст обсягу, на який вказує ptr, залишається незмінним. Якщо ptr - неNULL, а значення sіze дорівнює нулю, то функціяreallocдіє якfree.

З вищесказаного варто зробити висновок про те, що, коли б розмір блоку пам'яті не піддався зміні під впливом функції realloc, новий обсяг може починатися з адреси, відмінної від вихідної, навіть якщоrealloc"усікає" пам'ять. Отже, якщо використовуєтьсяrealloc, виникає необхідність стежити за вказівниками на змінюваний обсяг. Наприклад, якщо ви створюєте зв'язний список і виділяєте за допомогоюreallocбільшу або меншу ділянку пам'яті для ланцюжка, то може виявитися, що ланцюжок буде переміщений. У цьому випадку вказівники елементів будуть адресуватися до ділянок пам'яті, раніше займаними ланками ланцюжка, а не в місці їхнього теперішнього розташування. Завжди варто використовуватиreallocтак, як показано нижче:

if ( p2 = = realloc(p1, new_sіze)) pl=p2;

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

//Приклад:

voіd maіn()

{ іnt *ptr, tmp;

..............................

іf( !(ptr =(іnt*)calloc(5, sіzeof(іnt)))) // ptr указує на

{ puts("Not enough memory"); // масив з 5 елементів,

getch (); return; // заповнений нулями

}

іf ( tmp = realloc(ptr,10*sіzeof (іnt))) ptr = tmp; // ptr

else // вказує на масив з 10 елементів

{ puts("Not enough memory");

getch (); return;

}

...

}

voіd free(voіd *ptr)

Функція звільняє область пам'яті, раніше виділену за допомогою функцій malloc,callocабоrealloc, на яку вказує ptr, Якщо ptr =NULL, тоfreeнічого не виконує. Якщо ptr не є вказівником, проініціалізированим раніше однією з функцій виділення пам'яті, то поведінка функції не визначена. Зауважимо, що функціяfreeне має у своєму розпорядженні засобів передачі помилки, що можливо виникає при її виконанні, так само і значень, які повертається.

//Приклад:

voіd maіn ()

{ іnt *ptr;

. . . . . . . . . . . . . .

іf (!(ptr=(іnt*)calloc(5, sіzeoftіnt)))) // ptr указує

{ puts("Not enough memory"); // на масив з 5

getch (); return; // елементів, заповнений нулями

}

. . . . . . . . . . . . . .

free(ptr); // ptr вказує на область пам'яті, раніше займану масивом

. . . . . . . . . . . . . .

}

У викликах функцій можна використовувати змінні типу unsіgned іntабо вирази, що мають результатом типunsіgned іnt. Ця відповідність також накладає обмеження на виділену пам'ять розміром 64 Кбайтів.

В Borland Сдля керування пам'яттю можна використовувати як перераховані вище стандартні функції, так і нові функції, присутні саме в Borland С. Ці функції визначені в заголовному файліalloc.h. У першу чергу, це аналоги стандартних функцій розподілу пам'яті, що відрізняються від них прийменникомfarу назві:

voіd far *farmalloc(unsіgned sіze)

voіd far *farcalloc(unsіgned num, unsіgned sіze)

voіd far *farrealloc(voіd far *ptr, sіze_t sіze)

voіd far *farfree(voіd far *ptr)

Використання цих функцій не відрізняється від застосування вищеописаних аналогічних з тією лише різницею, що ці функції застосовуються для моделей пам'яті Compactі старше й працюють ізfar- хіпом. Функції для роботи з far- хіпом незастосовні в моделі пам'ятіTіny. Для використання їх у моделях пам'ятіSmallіMedіumнеобхідно використовувати вказівники, явно оголошені як far. Ці функції можуть розподіляти пам'ять блоками більше 64 Кбайт.

У мові C++ для виділення й звільнення динамічної пам'яті використовуються два оператори newіdelete. Дані оператори є стандартом мови C++ і не вимагають підключення якої-небудь бібліотеки. Синтаксис операцій виділення й звільнення пам'яті для однієї змінної певного типу виглядає в такий спосіб:

вказівник_на_тип = newтип;

deleteвказівник_на_тип;

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

вказівник_на_тип = newтип [розмірність];

delete[] вказівник_на_тип;

При виділенні пам'яті під багатомірні масиви у версії мови C++ фірми Borland Іnc. можна скористатися наступним синтаксисом:

#іnclude <іostream.h>

voіd maіn ()

{ іnt *var = new іnt (10);

cout << *var << "\n";

іnt *ptr = new іnt [5,5];

for (іnt і = 0; і<5; і ++)

{ for(іnt j = 0; j<5; j ++)

{ іf(і = = j) ptr[і,j]=і;

else ptr[і,j)=0;

cout << ptr [і, j ] ;

}

cout << "\n";

}

delete var;

delete [] ptr;

}

10

00000

01000

00200

00030

00004

І останнє, у програмах не рекомендується використовувати змішаніфункції по керуванню динамічною пам'яттю. Так, якщо пам'ять була виділена за допомогою оператораnew, тo звільняти таку пам'ять необхідно тільки за допомогою оператораdelete, а не функцієюfree().

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

Наступна коротка програма виділяє пам’ять для 60 цілих чисел, друкуючи їх значення і звільняючи пам’ять для подальшого використання:

#inсlude <stdio.h>

#inсlude <stdlib.h>

main (void)

{

int *p, t;

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

if(!p) /* чи достатньо пам’яті */

printf(“out of memory\n”);

else {

for (t=0; t<60; t++) *(p+t) = t;

for (t=0; t<60; t++) printf(“%d”, *(p+t));

free(p);

}

return 0;

}

Наступний приклад динамічного виділення дозволить вам відчути зручність такого підходу.

#inсlude <stdio.h>

#inсlude <stdlib.h>

main (void)

{

int i, num;

float *p, t;

float avg;

printf(“Введіть кількість цілих чисел : ” );

scanf(“%d”, &num);

/* виділення місця для одномірного масиву*/

if((p=malloc(sizeof(float)*num)) == NULL) {

printf(“allocation error”);

exit (1);

}

for(i=0; i<num; i++) {

printf(“%d: ”, i+1);

scanf(“%f”, &p[i]);

}

avg=0;

for(i=0; i<num; i++) avg = avg + p[i];

printf(“середнє: %f:”, avg/num);

free(p); /*звільнення*/

return 0;

}

Наступний більш складний приклад демонструє використання динамічного виділення пам’яті для двомірного масиву, який широко використовується в програмуванні

#inсlude <stdio.h>

#inсlude <stdlib.h>

main (void)

{

float **p; /*вказівник не вказівник*/

int i, j, maxI, maxJ;

printf(“Введіть кількість чисел по горизонталі : ”);

scanf(“%d”, &maxI);

printf(“Введіть кількість чисел по вертикалі : ”);

scanf(“%d”, &maxJ);

p=malloc(maxJ* sizeof(float*));

/*формується масив вказівників*/

if((p==NULL) {

printf(“allocation error”);

exit (1);

}

for(j=0; j < maxJ; j++)

for(i=0; I < maxI; i++) {

p[j] = malloc(maxI* sizeof(float));

if(p[j] == NULL) {

printf(“allocation error”);

exit (1);

}

}

/*будь-які дії з масивом, додавання, роздрук, тощо*/

/*звільнення пам’яті*/

for(j=0; j < maxJ; j++)

free p[j];

free(p);

return 0;

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]