Страницы

Поиск по вопросам

вторник, 2 октября 2018 г.

Откуда C/C++ знает сколько надо освободить памяти, если не знает размер массива?

В C/C++ для того чтобы обработать массив нужно знать его размер. Соответственно нужно всегда этот размер "помнить" и протаскивать во все функции обработки в качестве аргумента. Например:
void foo(int* arr, size_t n) { for (size_t i = 0; i < n; i++) { arr[i] = i * i; } }
Но при освобождении ресурсов знать размер почему-то необязательно. Можно просто вызвать free(arr) если память выделялась через malloc() или calloc(). Или можно использовать оператор delete[] arr; если память выделялась через оператор new int[n]
Вопрос откуда C/C++ знает сколько надо освободить памяти, если не знает размер массива? Функция free() и оператор delete[] не принимают в качестве аргументов размер массива, а только указатель на массив. А если C/C++ может каким-то образом вычислить размер, то зачем его постоянно "таскать" с собой в отдельной переменной?


Ответ

Все это - детали реализации.
В популярных реализациях malloc обычно записывает размер выделенного блока в начало выделенного блока. Возвращенный вам указатель обычно указывает на память сразу за этим записанным размером. free знает, где искать размер блока, и извлекает его именно оттуда.
По умолчанию new и delete просто делегируют запросы на выделение и освобождение сырой памяти в тот же самый malloc и free или их аналоги, через посредство operator new и operator delete.
При работе с массивами объектов с тривиальными деструкторами new[] и delete[] фактически ведут себя точно так же: вызывают в конечном итоге malloc с правильно вычисленным общим размером массива и вызывают free для освобождения памяти.
При работе с массивами объектов с нетривиальными деструкторами все несколько сложнее: new[] дополнительно записывает в начало блока памяти точное количество элементов создаваемого массива, а delete[] потом извлекает это количество и вызывает правильное количество деструкторов.
Допустим, если у вас есть какой-то класс MyNonTrivialClass размером в 9 байт с нетривиальным деструктором, то выполнение
MyNonTrivialClass *p = new MyNonTrivialClass[17];
приведет к формированию блока памяти со следующей внутренней структурой
+-----+-----+------+------+------ | 176 | 17 | p[0] | p[1] | ... +-----+-----+------+------+------ ^ ^ ^ | | | | | p - полученный вами указатель | | | поле типа `size_t` (8 байт), записано `new[]` | поле типа `size_t` (8 байт), записано `malloc` `new[]` запросил 161 байт = 17 * 9 + 8, размер выровнен до границы 16 байт
Конкретные значения могут отличаться, но общая идея обычно в популярных реализациях именно такая.
См. также https://ru.stackoverflow.com/a/770300/182825

Комментариев нет:

Отправить комментарий