Функції динамічного розподілу

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

Пам'ять, що виділяється в С функціями динамічного розподілу даних, знаходиться в т.зв. динамічно розподіляє області пам'яті (heap) [1]. Динамічно розподіляється область пам'яті - це вільна область пам'яті, не яка програмою, операційною системою або іншими програмами. Розмір динамічно розподіляє області пам'яті заздалегідь невідомий, але як правило в ній досить пам'яті для розміщення даних програми. Більшість компіляторів підтримують бібліотечні функції, що дозволяють отримати поточний розмір динамічно розподіляє області пам'яті, проте ці функції не визначені в Стандарті С. Хоча розмір динамічно розподіляє області пам'яті дуже великий, все ж вона конечна і може бути вичерпана.

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

Прототип функції malloc () наступний:

Тут колічество_байтов - розмір пам'яті, необхідної для розміщення даних. (Тип size_t визначено в як деякий цілий без знаку.) Функція malloc () повертає покажчик типу void *. тому його можна привласнити покажчику будь-якого типу. При успішному виконанні malloc () повертає покажчик на перший байт безперервного ділянки пам'яті, виділеного в динамічно розподіляє області пам'яті. Якщо в динамічно розподіляє області пам'яті недостатньо вільного місця для виконання запиту, то пам'ять не виділяється і malloc () повертає нуль.

При виконанні наступного фрагмента програми виділяється безперервний ділянку пам'яті об'ємом 1000 байтів:

Після присвоєння покажчик p посилається на перший з 1000 байтів виділеної ділянки пам'яті.

У наступному прикладі виділяється пам'ять для 50 цілих. Для підвищення мобільності (переносимості програми з однієї машини на іншу) використовується оператор sizeof.

Оскільки динамічно розподіляється область пам'яті не нескінченна, при кожному розміщенні даних необхідно перевіряти, чи відбулося воно. Якщо malloc () не змогла з якої-небудь причини виділити необхідний ділянку пам'яті, то вона повертає нуль. У наступному прикладі показано, як виконується перевірка успішності розміщення:

Звичайно, замість виходу з програми exit () можна поставити будь-якої обробник помилки. Обов'язковою тут можна назвати лише вимога не використовувати покажчик р. якщо він дорівнює нулю.

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

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

Підсистема динамічного розподілу в С використовується спільно з покажчиками для створення різних програмних конструкцій, таких як зв'язкові списки і виконавчі дерева. Кілька прикладів використання таких конструкцій наведені в частині IV. Тут розглядається інше важливе застосування динамічного розміщення: розміщення масивів.

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

Досить часто виникає необхідність виділити пам'ять динамічно, використовуючи malloc (). але працювати з цією пам'яттю зручніше так, ніби це масив, який можна індексувати. В цьому випадку потрібно створити динамічний масив. Зробити це нескладно, тому що кожен покажчик можна індексувати як масив. У наступному прикладі одновимірний динамічний масив містить рядок:

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

Можна також динамічно виділити пам'ять для багатовимірного масиву. Для цього потрібно оголосити покажчик, що визначає все, крім самого лівого вимірювання масиву. У наступному прикладі [2] двомірний динамічний масив містить таблицю чисел від 1 до 10 в ступені 1, 2, 3 і 4.

Програма виводить на екран наступне:

Покажчик р в головній програмі (main ()) оголошений як

Як згадувалося раніше, в C ++ потрібно перетворювати типи вказівників явно. Тому щоб дана програма була правильною і в С, і в C ++, необхідно виконати явне приведення типу значення, що повертається функцією malloc (). Для цього рядок, в якій вказівником р присвоюється це значення, потрібно переписати таким чином:

Багато програмістів використовують явне перетворення типів покажчиків для забезпечення сумісності з C ++.

[1] Застосовуються й інші назви: динамічна область. динамічно розподіляється область. купа. невпорядкований масив (даних).

Схожі статті