Использование realloc (X, 0) вместо free() и использование malloc с длиной строки +1

Так что я действительно не знаю, как поставить название на этот раз. Прежде всего, я хотел бы сказать, что я видел несколько комментариев на этой странице с предупреждением, если вопрос связан с «домашним заданием». Моя есть, но она тоже завершена, и я просто хочу еще больше понять, что происходит с кодом.

Я также некоторое время читал посты и книги, но думаю, что мне все еще чего-то не хватает.

У меня есть 2 строки кода, которые я не совсем понимаю в коде, с которым я работал. Работа заключается в том, чтобы получить любой файл, используемый в качестве аргумента (если это 0 файлов, он читается со стандартного ввода) и вывести его на стандартный вывод в обратном порядке. Все это, говоря о C, когда я пытался поставить тег.

Первая проблема такова:

array = realloc (array, 0);

Где массив определяется как

char **array;

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

Второй:

char* alloc = malloc (strlen ((char*)string)+1);

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

И вопрос в том, почему я должен использовать этот +1. Я имею в виду, что если я не использую по какой-то причине, это не работает, я пробовал с разными номерами, и это работает каждый раз, но если я не делаю этого +1, это не работает правильно.

Я знаю, что, вероятно, вопрос слишком расплывчатый и плохо написан, чтобы на него действительно можно было ответить, но опять же, я не уверен в этом, и я сделал все возможное, чтобы объяснить себя (английский не родной язык, поскольку это, вероятно, очевидно).


person keont    schedule 26.05.2013    source источник
comment
realloc(array, 0) не эквивалентно free(array). Эта идиома просто неверна. У меня нет времени, чтобы написать ответ в данный момент, но, надеюсь, кто-то может объяснить это хорошо.   -  person R.. GitHub STOP HELPING ICE    schedule 26.05.2013
comment
Как вы пришли к выводу, что free не работает?   -  person Theodoros Chatzigiannakis    schedule 26.05.2013
comment
Объясните, пожалуйста, что имеется в виду под не работает. Программа вылетает? Какие сообщения об ошибках печатаются?   -  person n. 1.8e9-where's-my-share m.    schedule 26.05.2013
comment
Обратите внимание, что семантика free состоит в том, чтобы делать память доступной для последующего выделения и не уменьшать значение счетчика свободного места, о котором сообщает ps, memfree или аналогичная системная утилита.   -  person Jens    schedule 26.05.2013
comment
@R.. Я действительно не могу защитить себя, я знаю, что читал, что это работает так же, или я так понял. Итак, ссылки: linux.die.net/man/3/realloc Realloc( ) возвращает указатель на вновь выделенную память, который соответствующим образом выровнен для любого типа переменной и может отличаться от ptr или NULL в случае сбоя запроса. Если размер был равен 0, возвращается либо NULL, либо указатель, подходящий для передачи в free(). Если realloc() дает сбой, исходный блок остается нетронутым; он не освобождается и не перемещается.   -  person keont    schedule 26.05.2013
comment
@н.м. Под не работает я подразумеваю, что произошел сбой (проблема могла быть где-то еще), но когда я изменил его на realloc (X, 0), он действительно работал в смысле удаления используемой динамической используемой памяти.   -  person keont    schedule 26.05.2013
comment
Если ваша программа дает сбой free, в вашем коде есть ошибка. realloc(X, 0) может быть или не быть эквивалентным free(X). Это зависит от реализации. В вашей системе они явно не эквивалентны, потому что free дает сбой, а realloc нет. Отключив сбой, вы просто сделаете вид, что ошибки не существует. Тем не менее, он все еще там. Я бы рекомендовал попробовать найти.   -  person n. 1.8e9-where's-my-share m.    schedule 26.05.2013
comment
Кстати, array = realloc(array, newSize); плохо. Если realloc не удается, старая память не будет freed, а будет возвращена NULL, которая стирает старый указатель, оставляя вас с утечкой памяти. Правильно tmp = realloc(array, newSize); if (tmp == NULL) {Do something appropriate here, keeping in mind that array still points to the original block and is still valid.} else {array = tmp;}.   -  person Joshua Green    schedule 27.05.2013


Ответы (6)


Поведение realloc, когда размер равен 0, отличается в C11 (текущая версия). Стандарт говорит (7.20.3.1 для C11, 7.22.3.1 для C1x)

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

Итак, используйте free и не полагайтесь на realloc.

При работе со строками через char* всегда не забывайте включать один дополнительный символ для нулевого терминатора \0. Это обычный способ показать, где заканчивается строка (другой способ — явная длина строки).

При использовании malloc и free помните, что они должны точно совпадать. Вам нужно free указать точный указатель (значение), возвращаемый malloc или realloc.

person Andrei    schedule 26.05.2013

массив = realloc (массив, 0);

Realloc с нулевым размером эквивалентен free() в некоторых реализациях C, но не все.

И проблема в том, что бесплатная не работает, она не освобождает используемое пространство

Подумайте, что означает char **array и как он распределяется в вашем приложении. Часто указатели на указатели используются как двумерные массивы, выраженные в виде массива массивов. Большинство приложений выделяют эти множественными вызовами malloc(). array — это просто массив из char *, где каждый элемент этого массива представляет собой массив из char. Простой вызов free() для массива char * освободит массив char *, но не каждый из массивов char.

Вам нужно вызвать free() несколько раз, как описано здесь.

Я пробовал с разными номерами, и это работает каждый раз, но если я не делаю этого +1, это не работает правильно.

Строки C имеют нулевое завершение, что означает, что программа отслеживает, где заканчивается строка, помещая нулевой символ в конец строки. Это означает, что строка C длиной N требует места для N символов плюс один нулевой символ. Тогда общая длина области памяти равна N+1.

person Marc Brooker    schedule 26.05.2013
comment
Если бы free(p) не сработало из-за описанного вами сценария, то realloc(p, 0) тоже не сработало бы. - person alk; 26.05.2013
comment
realloc(p,0) не эквивалентен free(p) в C11 - person Andrei; 26.05.2013
comment
realloc(p,0) эквивалентен free(p) только в Linux, а не в стандартном C и не в POSIX. - person hpsMouse; 26.05.2013
comment
Исправил утверждения в посте про realloc и free. - person Marc Brooker; 26.05.2013
comment
Если это эквивалентно Linux, это ошибка. realloc(p,0) формально эквивалентен free(p); malloc(0);, а в Linux (ну, glibc) malloc(0) возвращает уникальный ненулевой указатель для каждого вызова, а не нулевой указатель. - person R.. GitHub STOP HELPING ICE; 26.05.2013

Первый вопрос:

realloc(array, 0) не эквивалентно free(array).

Стандарт (C99, §7.20.3.4 ¶1) гласит:

Функция realloc освобождает старый объект, на который указывает ptr, и возвращает указатель на новый объект, размер которого указан size.

и не дает "особого случая" для size==0; таким образом, вы получаете указатель на объект нулевого размера, но который потенциально все еще является объектом и все еще должен быть освобожден.

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

На самом деле стандарт определяет size==0 поведение, определяемое реализацией, для всех функций выделения памяти, а не только для malloc, как я помнил; таким образом, поведение определяется реализацией, как описано ниже:

Более интуитивно, realloc «концептуально эквивалентно» malloc+memcpy+free для другого указателя, а malloc-обработка 0-байтового фрагмента памяти возвращает либо NULL, либо уникальный указатель, который нельзя использовать для хранения чего-либо (вы спросили для 0 байт), но все еще должен быть freeed. Так что нет, не используйте realloc таким образом, он может работать в некоторых реализациях (а именно, в Linux), но это точно не гарантируется.

Кроме того, непонятно, как вы пришли к выводу, что free не работает. Я могу придумать два способа убедить вас в этом:

  1. значение array и данных, на которые он указывает, не изменилось;
  2. выделенная память в диспетчере задач/top/во что бы то ни стало не уменьшается.

Для первого случая это нормально; когда вы освобождаете указатель, он не стирается волшебным образом — ваш указатель по-прежнему указывает туда, куда он указывал, но эта память больше не ваша — теперь она возвращается к среде выполнения C, которая, вероятно, повторно отдаст ее в будущем. malloc. Вот почему эта штука называется "висячий указатель", и многие люди после free устанавливают его на NULL, чтобы избежать повторной записи в области памяти, которая уже была освобождена.

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

Кстати, помните, что если вы char ** array содержите указатели на вещи, выделенные с помощью malloc, вы должны сначала free их , иначе у вас будет утечка памяти.


Второй вопрос:

Строки C заканчиваются нулем, т. е. последним символом строки всегда является \0, обозначающий конец строки, а strlen указывает длину строки без нулевого ограничителя. Таким образом, если вы не добавите этот +1, вы выделяете на char меньше памяти, чем требуется для фактического хранения вашей строки.


Приложение

Под не работает я подразумеваю, что произошел сбой (проблема могла быть где-то еще), но когда я изменил его на realloc (X, 0), он действительно работал в смысле удаления используемой динамической используемой памяти.

Как говорится на странице руководства,

Сбои в malloc(), calloc(), realloc() или free() почти всегда связаны с повреждением кучи, например, с переполнением выделенного фрагмента или двойным освобождением одного и того же указателя.

Вероятно, у вас есть какая-то другая ошибка в вашем коде, но, не видя ее, невозможно сказать, что и где идет не так.

person Matteo Italia    schedule 26.05.2013
comment
Стандарт предусматривает особый случай для size==0, просто он одинаков для всех функций управления памятью. В черновике n1256 он находится в разделе 7.20.3.1. - person Andrei; 26.05.2013
comment
@Andrei: вы правы, к счастью, это то, что я уже сказал о malloc :) исправляю ответ... - person Matteo Italia; 26.05.2013

По второму вопросу:

В C строка должна заканчиваться нулевым символом '\0'. Когда вы используете strlen, этот символ не учитывается, но вам нужно выделить для него достаточно места (это +1).

Если вы попытаетесь напечатать строку, не содержащую нулевой символ, может быть напечатано несколько «случайных» символов, но это также может привести к сбою. Если вы используете strcpy, вы сделаете переполнение буфера.

person Maxime Chéramy    schedule 26.05.2013
comment
Хорошо, спасибо, я должен был понять это сам, так как в тесте мы ищем такие строки... Это был мой недостаток, моя проблема заключалась в том, что я начал писать +8, затем +9, потому что я думал, что это дело количества битов. Теперь я верю, что понял. - person keont; 26.05.2013

Если вы надеетесь сохранить совместимость, realloc(p,0) никогда не будет эквивалентно free(p), а нулевое выделение памяти без последующего освобождения — это обычная утечка памяти, даже в Linux.

/* leak.c */
#include <mcheck.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
    void* p;
    mtrace();
    p = malloc(0x100);
    p = realloc(p, 0);
    exit(EXIT_SUCCESS);
}

$ cc -g leak.c -o leak
$ export MALLOC_TRACE=/tmp/t
$ ./leak
$ mtrace ./leak $MALLOC_TRACE
person Mr. Magoo    schedule 11.09.2013

Проблема заключается в char ** array !! Как это построено? Похоже, это не непрерывный блок памяти, и он был построен так:

char **array  = malloc(sizeof(char*)*size);
for (size_t i=0; i< size; i++) {
    array[i] = malloc(strlen(string)+1);
}

Если это так, то каждый индекс массива имеет свой индивидуальный указатель на случайные блоки памяти! В этом случае перед очисткой массива вам необходимо освободить каждый отдельный индекс!!!

for (size_t i=0; i<size; i++) {
    free(array[i]);
}
free(array);

Также с realloc NULL также может означать сбой, поэтому вы висите на старом указателе!. никогда не делай

foo= realloc(foo, new_size);

do

if (tmp = realloc(foo, new_size) ) {
     foo = tmp;
} else {
   // clean your ram, how do you handle failure?
   free(foo);foo = NULL;
   // or you're ok without the new size,or try to realloc again
   // up to you
}
person stderr    schedule 16.08.2018