Могу ли я предположить, что вызов realloc с меньшим размером освободит остаток? - PullRequest
26 голосов
/ 06 марта 2012

Давайте рассмотрим этот очень короткий фрагмент кода:

#include <stdlib.h>

int main()
{
    char* a = malloc(20000);
    char* b = realloc(a, 5);

    free(b);
    return 0;
}

Прочитав man-страницу для realloc, я не был полностью уверен, что вторая строка приведет к освобождению 19995 дополнительных байтов. Процитирую man-страницу: The realloc() function changes the size of the memory block pointed to by ptr to size bytes., но из этого определения могу ли я быть уверен, что остальные будут освобождены?

Я имею в виду, блок, на который указывает b, безусловно, содержит 5 свободных байтов, поэтому достаточно ли для ленивого компилятора-компилятора просто ничего не делать для строки realloc?

Примечание. Кажется, что используемый мной распределитель освобождает 19 995 дополнительных байтов, как показывает valgrind при комментировании строки free(b):

==4457== HEAP SUMMARY:
==4457==     in use at exit: 5 bytes in 1 blocks
==4457==   total heap usage: 2 allocs, 1 frees, 20,005 bytes allocated

Ответы [ 5 ]

25 голосов
/ 06 марта 2012

Да, гарантируется стандартом C, если новый объект может быть выделен.

(C99, 7.20.3.4p2) "Функция realloc освобождает старый объект, на который указывает ptr, и возвращает указатель на новый объект, размер которого определяется размером."

19 голосов
/ 06 марта 2012

Да, если это удастся.

Ваш фрагмент кода показывает известную, гнусную ошибку:

char* b = (char*) realloc(a, 5);

Если это удастся, память, которая ранее была выделена для aбудет освобожден, и b будет указывать на 5 байтов памяти, которые могут перекрывать или не перекрывать исходный блок.

Однако , если вызов не будет выполнен, b будет null, но a по-прежнему будет указывать на исходную память, которая все еще будет действительна.В этом случае вам нужно free(a), чтобы освободить память.

Еще хуже, если вы используете общую (опасную) идиому:

a = realloc(a, NEW_SIZE);     // Don't do this!

Если вызов realloc терпит неудачу, a будет null и его исходная память будет потеряна, что сделает ее безвозвратно потерянной до выхода из вашей программы.

3 голосов
/ 06 марта 2012

Это зависит от вашей реализации libc. Все следующее соответствует поведению:

  • ничего не делая, т.е. оставляя данные там, где они есть, и возвращая старый блок, возможно, повторно используя теперь неиспользуемые байты для дальнейшего распределения (на самом деле такое повторное использование не распространено)
  • копирование данных в новый блок и освобождение старого обратно в ОС
  • копирование данных в новый блок и сохранение старого для дальнейшего распределения

Также возможно

  • возвращает нулевой указатель, если новый блок не может быть выделен

В этом случае старые данные также останутся там, где они есть, что может привести к утечкам памяти, например, если возвращаемое значение realloc() перезаписывает единственную копию указателя на этот блок.

Разумная реализация libc будет использовать некоторую эвристику, чтобы определить, какое решение наиболее эффективно.

Также имейте в виду, что это описание находится на уровне реализации: семантически realloc() всегда освобождает объект, пока распределение не завершится неудачей.

1 голос
/ 06 марта 2012

Функция realloc имеет следующий контракт:

void * result = realloc (ptr, new_size)

  • Если результат равен NULL, то ptr по-прежнему действителен и неизменен.
  • Если результат не равен NULL, то ptr теперь недействителен (как если бы он был освобожден) и больше никогда не должен использоваться.Теперь результат - это указатель на точно новые байты данных.

Точные детали того, что происходит внутри, зависят от реализации - например, результат может быть равен ptr (но дополнительное пространство за пределами new_sizeнельзя больше трогать) и realloc может вызывать free или может делать свое собственное внутреннее свободное представление.Ключевым моментом является то, что как разработчик вы больше не несете ответственности за ptr, если realloc возвращает ненулевое значение, и вы все равно несете ответственность за него, если realloc возвращает NULL.

0 голосов
/ 06 марта 2012

Кажется маловероятным, что 19995 байт были освобождены. Более вероятно, что realloc заменил 20000-байтовый блок другим 5-байтовым блоком, то есть 20000-байтовый блок был освобожден и был выделен новый 5-байтовый блок.

...