Моделювання одновимірних масивів з використанням динамічної пам'яті
Необхідність використання коштів роботи з динамічною пам'яттю при обробці масивів виникає у випадках, коли статична реалізація неефективна (наприклад, розміри масиву істотно розрізняються в залежності від використання програми і резервування пам'яті максимально можливого обсягу "на всі випадки життя" нераціонально). При цьому суть поняття "масив" зберігається: це сукупність однотипних елементів, що має єдине ім'я; елементи розташовані в певному порядку і розрізняються індексами; над цією сукупністю можливі будь-які операції, що допускаються типом елементів.
Перераховані властивості досить просто моделюються засобами динамічної пам'яті. Таким чином, інтерес представляють аспекти програмної реалізації, які і розглядаються нижче.
У мові Сі ім'я статичного масиву є вказівником-константою на перший байт першого елемента масиву. Так, опис int a [5] може бути проілюстровано наступним чином:
Таким чином, звернутися до елементу масиву можна двома способами, через покажчик і індексуючи елемент:
І в цьому випадку звернутися до елементу поля можна також двома способами, через покажчик і індексуючи елемент:
Пам'ять виділяється принципово по-різному, а обробляється однаково. Зауважимо, що було б грубою помилкою вважати, що опису
Так, наприклад, після виконання оператора
pa = (int *) malloc (10 * sizeof (int));
Певні вище масив a і покажчик pa стають в повному сенсі еквівалентними. Однак друге рішення буде більш гнучким, бо тут затребувана пам'ять виділяється динамічно в процесі виконання програми і може бути при необхідності повернута системі за допомогою функції free (), чого не можна зробити в разі масиву.
Розглянемо стандартні функцію malloc () для динамічного виділення пам'яті під зберігання елементів масиву.
У багатьох задачах обчислювальної математики і при реалізації алгоритмів обробки інформаційних структур виникає потреба роботи з масивами, кількість елементів яких змінюється від одного прогону програми до іншого.
Найпростіше рішення цієї проблеми полягає в статичному описі відповідних масивів із зазначенням максимально необхідної кількості елементів. Однак такий підхід призводить, як правило, до невиправданого завищення обсягу пам'яті, необхідної для роботи програми. Альтернативне рішення відкривається в зв'язку з використанням покажчиків для подання масивів змінних.
Нехай нам необхідно написати програму скалярного множення векторів A і B, розмірність яких заздалегідь не відома. Для цього поступимо таким чином. Опишемо в заголовку програми змінну m, визначальну довжину відповідних масивів, і покажчики a, b, c, які будуть визначати розміщення в пам'яті векторів-співмножників і вектора-результату:
Після того, як значення m буде визначено (воно може бути, наприклад, введено з клавіатури терміналу), необхідно виділити достатній обсяг пам'яті для зберігання всіх трьох векторів. Оскільки мова тут йде про динамічному розміщенні масивів в процесі виконання програми, ми повинні скористатися однією з трьох спеціальних функцій, що входять до складу стандартної бібліотеки malloc. h і відомості про яких наведені нижче.
malloc - резервує блок пам'яті розміром size байт; затребувана пам'ять звільняється після закінчення роботи програми або за допомогою функції free (див. нижче)
Формат і опис аргументів:
void * malloc (size)
int size; / * Необхідна кількість байт пам'яті * /
В описаному вище прикладі розмір масиву був вказаний як 10 * sizeof (int). Це означає що було виділено необхідну кількість байт для зберігання 10 змінних цілого типу. Функція sizeof визначає кількість байт для змінної одного зі стандартних типів (розміри пам'яті для зберігання змінних за типами наведені в таблиці п.1).
Для функції malloc повертає значення є покажчиком невизначеного типу на перший байт зарезервованої області статичної пам'яті і так само NULL при відсутності можливості виділити пам'ять необхідного розміру. Для отримання покажчика на конкретний тип даних, необхідно застосувати до поверненню значенням операцію явного перетворення типу. Наприклад: int * malloc (size).
Попередні опису всіх цих функцій поміщені в файли stdlib.h і malloc.h і при їх використанні один з них повинен бути включений до складу вихідного тексту програми за допомогою директиви препроцесора #include.
Вибравши в нашій задачі для розміщення масивів a, b і c функцією m alloc (), можна записати:
де операція приведення (float *) перетворює покажчик невизначеного типу в покажчик типу float. Тепер, після попереднього введення числових значень елементів векторів, може бути виконано їх скалярне множення:
for (i = 0; i Пам'ять, затребувана у системи шляхом використання функції malloc (), може бути повернута назад до повного завершення роботи програми за допомогою функції free (). Більш того, використовуючи функцію realloc (), можна змінити розмір раніше зарезервованого блоку пам'яті. Попередні опису функцій free () і realloc () також знаходяться в файлах stdlib () і malloc (), а необхідні відомості про них наведені нижче. free - звільняє блок пам'яті, попередньо зарезервований однією з функцій malloc () або realloc () Формат і опис аргументів: void * ptr; / * Покажчик на звільняється блок * / Ця функція в результаті своєї роботи не повертає ніякого значення. Крім того, вона ігнорує покажчик ptr, якщо він дорівнює NULL. realloc - змінює розмір блоку пам'яті, попередньо зарезервованого функціями malloc () або realloc (). Формат і опис аргументів: void * realloc (ptr. size) void * ptr; / * Покажчик на попередньо * / / * Зарезервований блок пам'яті * / int size; / * Новий розмір блоку в байтах * / Значення, що повертається є покажчиком невизначеного типу на перший байт зарезервованої області статичної пам'яті і так само NULL при відсутності можливості виділити пам'ять необхідного розміру. У загальному випадку воно не дорівнює значенню покажчика ptr, бо при зміні розміру блоку може також змінитися його розміщення в оперативній пам'яті ЕОМ. Для отримання покажчика на конкретний тип даних, необхідно застосувати до поверненню значенням операцію явного перетворення типу. Тепер, знаючи яким чином можна використовувати динамічну пам'ять для зберігання елементів масиву, можна написати текст програми до нашого завдання скалярного добутку двох векторів. Доречним буде також розібрати приклад зі зміною розміру одновимірного динамічного масиву в ході виконання програми. Для цього розглянемо задачу в якій вводяться послідовність оцінок (1-5) і вираховується середнє арифметичне. Кількість оцінок заздалегідь не відомо, кінець послідовності - 0. # include Схожі статті