МІНІСТЕРСТВО ОСВІТИ І НАУКИ, МОЛОДІ ТА СПОРТУ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ «ЛЬВІВСЬКА ПОЛІТЕХНІКА»
Кафедра САПР
Звіт
до лабораторної роботи №12
на тему «ДИНАМІЧНЕ ВИДІЛЕННЯ ПАМ’ЯТІ
В МОВІ ПРОГРАМУВАННЯ С»
з курсу «Проблемно-орієнтовані мови програмування»
ЛЬВІВ 2011
Мета роботи
Навчитися використовувати динамічне виділення памяті в мові С для роботи з масивами.
Теоретичні відомості
Одним із способів зберігання інформації є використання системи динамічного виділення пам'яті мови С. При цьому пам'ять виділяється з вільної області пам'яті в міру потреби й повертається назад, тобто звільняється, коли необхідність у ній зникла. Область вільної пам'яті, доступної для виділення, перебуває між областю пам'яті, де розміщається програма, і стеком. Ця область називається купою або хіпом (від англ.: 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 весь простір за програмним стеком наприкінці доступної пам'яті використовується для розподілу.
Функція calloc виділяє блок пам'яті й повертає вказівник на перший байт блоку. Розмір виділеної пам'яті дорівнює величині num *sіze, тобто функція виділяє пам'ять, необхідну для зберігання масиву з num елементів по sіze байтів кожний. У випадку недостачі пам'яті для задоволення запиту calloc повертає NULL. Виділена пам'ять ініціалізується нулями.
Ця функція змінює розмір динамічно виділеної області пам'яті, на яку вказує *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 обновляється при кожному новому виклику функції, що вказує на область пам'яті (можливо, нову).
Функція звільняє область пам'яті, раніше виділену за допомогою функцій malloc, calloc або realloc, на яку вказує ptr, Якщо ptr = NULL, то free нічого не виконує. Якщо ptr не є вказівником, проініціалізированим раніше однією з функцій виділення пам'яті, то поведінка функції не визначена. Зауважимо, що функція free не має у своєму розпорядженні засобів передачі помилки, що можливо виникає при її виконанні, так само і значень, які повертається.
Індивідуальне завдання
Ввести num - кількість масивів. Ввести розмірність чергового масиву і його елементи цілого типу, розмістити їх у динамічній пам’яті. Розсортувати масиви по зростанню і вивести на екран.
Текст програми
#include <iostream.h>
int main()
{
int i,j,m,*a,v=0,h;
int **pi;
cout<<"Kilkist ryadkiv:";
cin>>m;
a=new int(m);
pi=new int*[m];
for(i=0;i<m;i++)
{
cout<<"Kilkist elementiv v "<<v+1<<" ryadky:";
cin>>a[v];
pi[i]=new int [a[v]];
v++;
}
for(i=0;i<m;i++)
{
cout<<"Vvedite elementu "<<i+1<<" ryadka:\n";
for(j=0;j<a[i];j++)
cin>>(pi[i][j]);
}
cout<<"Vvedeni ryadki:\n";
for(i=0;i<m;i++)
{
for(j=0;j<a[i];j++)
cout<<(pi[i][j])<<" ";
cout<<"\n";
}
for(i=0;i<m;i++)
{
for(j=0;j<a[i];j++)
for(v=0;v<(a[i]-1);v++)
if(pi[i][v]>pi[i][v+1])
{
h=pi[i][v];
pi[i][v]=pi[i][v+1];
pi[i][v+1]=h;
}
}
cout<<"Vidsortovani ryadki\n";
for(i=0;i<m;i++)
{
for(j=0;j<a[i];j++)
cout<<(pi[i][j])<<" ";
cout<<"\n";
}
for(i=0;i<m;i++)
delete [] pi[i];
delete [] pi;
}
Результати обчислень
Висновок: Я навчився використовувати динамічне виділення памяті в мові С для роботи з масивами.