Доступ к памяти за пределами выделенного блока также ведет к неопределенному поведению, опять из-за того, что она может содержать учетную информацию или, возможно, вообще не принадлежать адресному пространству процесса. Запись в такой участок памяти гораздо хуже, поскольку это может уничтожить учетные данные.
Любая динамическая память, которая больше не нужна, должна быть освобождена. В частности, необходимо тщательно управлять памятью и освобождать ее, когда она выделяется внутри циклов или рекурсивных или глубоко вложенных вызовов функций. Отказ от этого ведет к
Даже если программа никогда не завершается из-за недостатка памяти, постоянно увеличивающиеся программы теряют производительность, поскольку операционная система должна сохранять использующиеся данные в физической памяти. В худшем случае, это может привести к поведению, известному как
Хотя free()
может вернуть освобожденную память системе и сократить адресное пространство процесса, это почти никогда не делается. Вместо этого освобожденная память готова для нового выделения при следующем вызове malloc()
, calloc()
или realloc()
.
При условии, что освобожденная память продолжает оставаться в адресном пространстве процесса, стоит обнулить ее перед освобождением. Например, такой способ может оказаться предпочтительным для программ с повышенными требованиями к безопасности.
Обсуждение ряда полезных инструментов для отладки динамической памяти см в разделе 15.5.2 «Отладчики выделения памяти».
3.2.1.4. Изменение размера: realloc()
Динамическая память имеет существенное преимущество перед статически объявленными массивами, поскольку это позволяет использовать столько памяти, сколько нужно, и не больше. Не нужно объявлять глобальный, статический или локальный массив фиксированного размера и надеяться, что он: (а) достаточно большой и (б) не слишком большой. Вместо этого можно выделить ровно столько, сколько нужно, не больше и не меньше.
Вдобавок, можно изменять размер динамически выделенной области памяти. Хотя можно сократить размер блока памяти, обычно его увеличивают. Изменение размера осуществляется с помощью realloc()
. Продолжая пример с coordinates
, типичный код выглядит следующим образом:
int new_count;
size_t new_amount;
struct coord *newcoords; /* установить, например: */
new_count = count * 2; /* удвоить размер памяти */
new_amount = new_count * sizeof(struct coord);
newcoords =
(struct coord*)realloc(coordinates, new_amount);
if (newcoords == NULL) {
/* сообщить об ошибке, восстановить или прервать */
}
coordinates = newcoords;
/* продолжить использование coordinates ... */
Как и в случае с malloc()
, шаги стереотипны по природе и сходны по идее.
1. Вычислить новый выделяемый размер в байтах.
2. Вызвать realloc()
с оригинальным указателем, полученным от malloc()
(или от calloc()
или предыдущего вызова realloc()
) и с новым размером.
3. Привести тип и присвоить возвращенное realloc()
значение. Подробнее обсудим дальше.
4. Как и для malloc()
,