Динамічний розподіл пам'яті в C

Динамічний розподіл пам'яті в C — це спосіб ручного керування пам'ятю в мові програмування C за допомогою функцій стандартної бібліотеки, а саме malloc, realloc, calloc, aligned_alloc та free. [1] [2]

Попри те, що ці функції наявні в C++, автори закликають використовувати більш безпечні оператори new та delete. [3] Хоча існують винятки, де їх використання не рекомендується. Серед них, чутливі до продуктивності ділянки коду або реалізація прибиральника сміття.

Існує безліч механізмів перерозподілу пам'яті, що базуються на функції malloc. Вони можуть відрізнятися за швидкістю виконання чи споживанню пам'яті.

Огляд функцій ред.

Функції динамічного розподілу пам’яті знаходяться у заголовку stdlib.h ( заголовок cstdlib в C++).

функція опис
malloc виділяє вказану кількість байтів
aligned_alloc виділяє вказану кількість байтів із вказаним вирівнюванням
realloc збільшує або зменшує розмір зазначеного блоку пам'яті, переміщуючи його за необхідності
calloc виділяє вказану кількість байтів та заповнює її нулями
free звільняє вказаний блок пам'яті назад до системи

Різниця між malloc() та calloc() ред.

  • malloc() приймає один аргумент (обсяг пам’яті для виділення в байтах), тоді як calloc() приймає два аргументи — кількість елементів і розмір кожного елемента.
  • malloc() виділяє лише пам’ять, тоді як calloc() виділяє та заповнює данну область пам'яті нулями. [4]

Приклад використання ред.

Створення масиву в С з десяти цілих чисел є доволі простим:

int array[10];

Але розмір масиву визначається під час компіляції. Якщо потрібно виділити його динамічно та без використання масиву змінної довжини, який присутній не в всіх реалізаціях C11, можна використати такий код:

int *array = malloc(10 * sizeof(int));

Він обчислює кількість байтів, які десять цілих чисел займають у пам’яті, потім виділяє їх через функцію malloc. Адреса виділеного блоку пам'яті зберігається у змінній array (завдяки синтаксису C, вказівники та масиви в деяких ситуаціях можуть використовуватись рівнозначно). Оскільки функція malloc не гарантує виділення пам'яті, вона може повернути нульовий вказівник у разі помилки. Перевіряти це в програмуванні вважається хорошою практикою:

int *array = malloc(10 * sizeof(int));
if (array == NULL) {
  fprintf(stderr, "malloc failed\n");
  return -1;
}

Після того, як динамічний масив більше не потрібен, потрібно викликати функцію free, щоб звільнити пам'ять, яку він займає:

free(array);

Пам'ять, що виділена malloc не є ініціалізована за замовчуванням та може містити залишки роботи попередніх програм. Тобто, елементи масиву виділеного за допомогою malloс є неініціалізованими змінними. Функція calloc повертає пам'ять, що очищується за замовчуванням.

int *array = calloc(10, sizeof(int));

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

int *arr = malloc(2 * sizeof(int));
arr[0] = 1;
arr[1] = 2;
arr = realloc(arr, 3 * sizeof(int));
arr[2] = 3;

Варто зазначити, що realloc може повернути вказівник на нову адресу. Тобто, якщо блок пам'яті, що належить вказівнику, не може бути розширеним, realloc виділить новий блок пам'яті та зкопіює туди старі дані. Тоді попередній вказівник буде більше не дійсним.

Джерела ред.

  1. Summit, Steve. Chapter 11: Memory Allocation. C Programming Notes. Процитовано 11 July 2020.
  2. aligned_alloc(3) - Linux man page.
  3. Stroustrup, Bjarne (2008). Programming: Principles and Practice Using C++. Addison Wesley. с. 1009. ISBN 978-0-321-54372-1.
  4. calloc(3) – Linux Programmer's Manual – Library Functions