Національний технічний університет України
«Київський політехнічний інститут імені Ігоря Сікорського»Кафедра АПЕПС
Алгоритмізація та програмування 2: . Процедурне програмування
ЗВІТ
До лабораторної роботи № 2
«Динамічне виділення пам’яті для одно- та двовимірних масивів»
Варіант №24
Дата «11» Квітня 2022
ЗАВДАННЯ:
1. Ознайомитись з особливостями роботи з динамічними одно- та двовимірними масивами.2. Розробити Блок-схему програмного алгоритму.3.Виконати індивідуальне завдання.4. Оформити ЗВІТ до лабораторної роботи згідно вимог та методичних рекомендацій.5. Вихідні дані (завдання) обрати згідно свого варіанта у Додатку B-2.
РЕЗУЛЬТАТ РОБОТИ:
1. Роздрукувати отримані результати (вивести на екран).2. ЗВІТ до комп’ютерного практикуму для перевірки додати в Клас.3. Програмний код розмістити на сайті Repl.it (посилання виключно через кнопку «+ Invite»).
Теоретичні відомості
Динамічний масив – це така структура даних, яка може створюватися тоді, коли кількість елементів наперед невідома, а також може змінювати свій розмір під час виконання програми.
Формування масивів зі змінними розмірами можна організувати за допомогою вказівників та засобів динамічного розподілу пам’яті. В мові С передбачені широкі можливості керування пам’яттю під час виконання програми, які забезпечуються
функціями, що розташовані в бібліотеці stdlib.h.
Для виділення і звільнення динамічної пам’яті використовуються наступні функції:
malloc()
void * malloc (unsigned s)
Повертає вказівник на початок області динамічної пам’яті довжиною в s байт, при невдалому завершенні повертає NULL.
calloc()
void * calloc (unsigned n, unsigned m)
Повертає вказівник на початок області динамічної пам’яті для розміщення n
елементів довжиною по m байт кожен, при невдалому завершенні повертає NULL. realloc()
void * realloc (void * p, unsigned s)
Змінює розмір блоку раніше виділеної динамічної пам’яті до розміру s байт, р –
адреса початку змінюваного блоку, при невдалому завершенні повертає NULL. free()
void * free (void p)
Звільняє раніше виділену ділянку динамічної пам’яті, р – адреса першого байту.
В усіх функціях виділення пам’яті необхідно передавати кількість байт, а зазвичай програмісту відома кількість елементів або структур, що будуть в ній розміщені. Для визначення дійсної кількості байт, що буде відведена під дану змінну або структуру в С введена функція sizeof(), яка повертає кількість байт в даному типі змінної або структури.
Функція для формування одновимірного динамічного масиву:
int * make_mas (int n)
{
int * mas;
mas = (int *) malloc (n * sizeof (int)); for (int i = 0; i <n; i ++)
mas [i] = random (10); return mas;
}
Для виділення пам’яті використовується функція malloc(), параметром якої є розмір ділянки, що виділяється пам’яті рівний n * sizeof (int). Так як функція malloc() повертає нетипізований вказівник void *, то необхідно виконати перетворення отриманого нетипізованого вказівника на вказівник типу int *.
Звільнити виділену пам’ять можна функцією free (mas).
Після виділення деякої ділянки пам’яті така ділянка резервується і не може бути повторно використана до тих пір, поки вона не буде звільнена за допомогою явної функції free(). Функція free(p) “знає” розмір ділянки, що пов’язана з вказівником, тому достатньо в ній вказати вказівник на відповідну ділянку. Після виконання функції free() всі вказівники на неї стають невизначеними і можуть бути використані для адресації інших ділянок. Якщо трапиться, що на дану ділянку не вказує жоден вказівник, вона стає недоступною, але не повертається до вільної частини купи і не може бути використаною. Саме така помилка частіше всього призводить до важко визначуваної помилки, що зветься “витік пам’яті” (memory leak).
Добрим правилом вважається звільняти динамічно виділені ділянки пам’яті в тому ж блоці коду, де вона була виділена, наприклад в функції main() або в будь-якій функції.
При виділенні динамічної пам’яті під масив розміри масиву повинні бути повністю визначені до операції виділення пам’яті.
Для створення двовимірного масиву можливі два підходи, кожен з яких має свої переваги і недоліки:
В першому випадку для виділення масиву M*N визначається повний розмір ділянки пам’яті під весь масив як arr_size = M*N*sizeof(arr_elem). Недоліком такого підходу є те, що безпосереднє адресування елементу a[i][j] неможливо, бо під час компілювання невідомі розміри масиву, і приходиться використовувати обчислення індексу одновимірного масиву j*M+i, але тут є і переваги – масив витрачає пам’ять лише на зберігання необхідних даних, суттєво спрощуються операції при зміні розмірів масиву і при запису масиву у бінарний файл.
В другому випадку, створюється одновимірний масив вказівників, які далі слугують вказівниками на нові одновимірні масиви.
// виділення динамічної пам’яті 100 * sizeof (int) байт int * a = (int *) malloc(100 * sizeof (int));
// Виділення динамічної пам’яті під двовимірний динамічний масив * / int ** form_matr (int n, int m)
{
int ** matr = (int **) malloc(n*sizeof(int*)); // виділення пам’яті під масив вказівників
for (int i = 0; i <n; i ++) // виділення пам’яті для масиву значень matr [i] = (int*) malloc(m*sizeof(int));
return matr; // повернення вказівника на масив вказівників
}
Видалення з динамічної пам’яті двовимірного масиву другого типу здійснюється в зворотному порядку, тобто спочатку звільняється пам’ять, виділена під одномірний масив з даними, а потім пам’ять, виділена під одномірний масив вказівників.
Перевагою такого підходу є можливість звернення до елементів масиву звичайним методом a[i][j], але розмір такої структури більший, а засоби керування розмірами складніші.
Варіант завдання
/
Результати програми
Вивід на екран монітора результату
/
Висновок:
Під час виконання цієї лабораторної робити було ознайомлено з методами динамічного виділення пам’яті для одно- та двовимірних масивів, їх перевагами та недоліками.
Результати були виведені на екран.
Силка на repl.it: https://replit.com/join/xflchnevkr-tr-15shiepietko