Поведение `realloc ()`, когда память сокращается - PullRequest
4 голосов
/ 13 марта 2019

Страница man из realloc() сообщает:

Функция realloc() изменяет размер блока памяти, на который указывает ptr, до size байтов,Содержимое будет неизменным в диапазоне от начала региона до минимума старого и нового размеров.Если новый размер больше старого, добавленная память не будет инициализирована.

Однако на странице руководства ничего не говорится о том, что происходит, если новый размер меньше старого.Например, если у меня есть следующий код:

ptr = realloc(ptr, nsize); // Where nsize < the original size and ptr is of type void **

Если исходный размер size, означает ли это, что ptr + nsize + 1 все еще содержит выделенные записи?

Любая помощь приветствуется.

Ответы [ 2 ]

8 голосов
/ 13 марта 2019

сначала вы, вероятно, имеете в виду:

void **ptr = malloc(nsize*2);

, затем

ptr = realloc(ptr, nsize);

или безопасный способ:

void **ptr2 = realloc(ptr, nsize);
if (ptr2 != NULL)
{
   ptr = ptr2;
} // else failure

, потому что с помощью realloc(ptr,nsize) до установите значение ptr - неопределенное поведение и, возможно, сбой.

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

Теперь ваш вопрос:

Если исходный размер size, означает ли это, что ptr + nsize + 1 по-прежнему содержит выделенные записи?

у вас нет на это гарантии.Это неопределенное поведение от ptr + nsize уже (спасибо Сурав).

Почему?эта область больше не принадлежит вашей программе.

Вы могли бы иметь ошибки при чтении нового массива меньшего размера, которые могли бы дать действительные результаты, если бы там были старые данные, что вероятно, правда, но:

  • система может сохранить ту же область памяти, но немедленно повторно использовать этот блок для других данных
  • система может переместить новые данные в другую область памяти (такая стараяptr будет отличаться от нового ptr, следовательно, возвращаемое значение, которое некоторые люди игнорируют, и оно «работает» до тех пор, пока не выйдет из строя), в этом случае за полными несущественными данными стоят

Если оба вышеуказанных условия не выполняются, вполне вероятно, что данные не изменились.realloc не будет устанавливать в 0 некоторую память, которая не должна использоваться.Некоторые отладочные фреймворки (которые я не помню) помещают шаблон при освобождении памяти, поэтому, если вы наткнетесь на этот шаблон в своей программе, это будет явным признаком того, что вы читаете нераспределенную / неинициализированную память, но у нее есть накладные расходы, поэтомуне сделано по умолчанию.Вы можете «перегрузить» функции выделения памяти, чтобы сделать это и сами.

В любом случае, убедитесь, что вы не читаете после нового массива, поскольку то, что вы найдете, не гарантировано.

5 голосов
/ 13 марта 2019

Прежде всего,

 void **ptr = realloc(ptr, nsize); 

неверно, так как вы используете ptr неинициализированным (что здесь определено) и согласно realloc() описанию функции из C11, глава§7.22.3.5

Если ptr является нулевым указателем , функция realloc ведет себя как функция malloc для указанного размера.В противном случае, если ptr не соответствует указателю, ранее возвращенному функцией управления памятью , или если пространство было освобождено при вызове функции free или realloc, поведение не определено.[...]

Итак, ваш код вызывает неопределенное поведение, когда вы передаете указатель, который содержит неопределенное значение.

Однако, учитывая ваш случайчто-то вроде

void **ptr = malloc(size);
assert (ptr);
ptr = realloc(ptr, nsize);

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

Тем не менее, еще раз проверьте кавычку ( emphasis mine )

Функция realloc() изменяет размер блока памяти, на который указывает ptr, размер байтов. Содержимое будет неизменным в диапазоне от начала региона до минимума старого и нового размеров. Если новый размер больше старого, добавленная память не будет инициализирована.

Итак, чтобы ответить

Если исходный размер равен size, означает ли это, что ptr + nsize + 1 все еще содержит выделенные записи?

Нет, мы не можем сказать.После успешного вызова realloc() нам разрешен доступ только до ptr + nsize - 1.Попытка чтения / записи ptr + nsize и далее не определена, так как эта область памяти больше не принадлежит вашему процессу, и эта область памяти «недопустима».

В любом случае вам не нужно беспокоиться о содержимом, превышающем ptr + nsize - 1.

...