Как освободить перераспределенную и выделенную память?

Как освободить память, которая когда-то была выделена вначале, а затем перераспределена и выделена сразу после этого? Этот ptr был моей попыткой, но valgrind говорит, что было 6 аллоков и 6 освобождений, но 90 байтов в 3 блоках определенно потеряны.

char *textInFile = (char *) calloc(currentLength + 1, sizeof(char) * currentLength);
char *currentLine = (char *) calloc(currentLength + 1, sizeof(char) * currentLineLength);
...
while ((textInFile[index] = getc(f)) != EOF) {
    if (index > currentLength - 3) {
        currentLength += 10;
        ptr = textInFile;
        textInFile = (char *) realloc(textInFile, currentLength);
        textInFile = (char *) calloc(currentLength, sizeof(char) * currentLength);
        free(ptr);
    }
    ...
    if (textInFile[index] == '\n') {
        int k = 0;
        for (int i = previousIndex; i < index; i++) {
            if (k > currentLineLength - 3) {
                currentLineLength += 10;
                ptr = currentLine;  
                currentLine = (char *) realloc(currentLine, currentLineLength);
                currentLine = (char *) calloc(currentLineLength, sizeof(char) * currentLineLength);
                free(ptr);
            }
    ...
    index++;
}
...
free(textInFile);
free(currentLine);

== 4426 == РЕЗЮМЕ КУЧИ:

== 4426 == используется на выходе: 90 байт в 3 блоках

== 4426 == общее использование кучи: 9 выделений, 9 освобождений, выделено 14 668 байт

==4426==

== 4426 == РЕЗЮМЕ УТЕЧКИ:

== 4426 == точно потеряно: 90 байт в 3 блоках

== 4426 == косвенно потеряно: 0 байт в 0 блоках

== 4426 == возможно потеряно: 0 байт в 0 блоках

== 4426 == все еще достижимо: 0 байт в 0 блоках

== 4426 == подавлено: 0 байт в 0 блоках


person tomashauser    schedule 21.11.2019    source источник


Ответы (4)


Вам нужно вызвать free() для каждого указателя, отличного от NULL, который был возвращен calloc(). Если вы вызываете realloc(), вы не должны вызывать free() для указателя, который вы передали в качестве аргумента для realloc(), но вы должны вызывать free() для указателя, который вернул realloc(). (Примечание: правильно использовать realloc() сложно, особенно если вы хотите правильно обрабатывать ошибки - я рекомендую избегать этого, если это не строго необходимо)

Одна проблема, в частности, с вашим кодом, заключается в следующем:

textInFile = (char *) realloc(textInFile, currentLength);
textInFile = (char *) calloc(currentLength, sizeof(char) * currentLength);

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

person Jeremy Friesner    schedule 21.11.2019
comment
Но разве ptr не выполняет свою работу? - person tomashauser; 21.11.2019
comment
Не после того, как вы перезапишете его новым значением, возвращенным из calloc(); как только вы это сделаете, ничего больше не будет указывать на буфер, возвращенный realloc(). Указатели не хранят список всех адресов памяти, которым они были ранее назначены. - person Jeremy Friesner; 21.11.2019

Я думаю .. что вы неправильно поняли realloc.

realloc не free.

Возвращает выделенную память ..

ваш код

        textInFile = (char *) realloc(textInFile, currentLength);
        textInFile = (char *) calloc(currentLength, sizeof(char) * currentLength);
        free(ptr);

textInFile был активирован дважды .. Утечка 1-го указателя ..

person Peter Lee    schedule 21.11.2019
comment
Извините, я забыл добавить конец кода. Я действительно их освобождаю. Это редактировалось. - person tomashauser; 21.11.2019
comment
@tomashauser хмм .. Прочтите внимательно мой ответ ... Вы выделили ДВАЖДЫ .. и ОДИН РАЗ бесплатно .. - person Peter Lee; 21.11.2019

Ответ:

ptr должен был указывать на realloc, который должен был быть освобожден. В вопросе исходный адрес textInFile был потерян, так как он был перезаписан.

ptr = (char *) realloc(currentLine, currentLineLength);
currentLine = (char *) calloc(currentLineLength, sizeof(char) * currentLineLength);
free(ptr);
person tomashauser    schedule 21.11.2019

Этот код не имеет смысла:

textInFile = (char *) realloc(textInFile, currentLength);
textInFile = (char *) calloc(currentLength, sizeof(char) * currentLength);

Почему вы звоните calloc после realloc? Как вы думаете, что делает этот код? Если вы пытаетесь обнулить расширенную память, это не сработает.

Если вы хотите расширить (realloc) буфер, вот общая процедура для этого. Предполагая, что вы выделили буфер как

T *buffer = calloc( number_of_items, sizeof *buffer ); // sizeof *buffer == sizeof (T)

для некоторого типа T вы бы расширили буфер следующим образом:

T *tmp = realloc( buffer, (number_of_items + number_of_new_items) * sizeof *buffer );
if ( tmp )
{
  buffer = tmp;
  number_of_items += number_of_new_items;
}
else
{
  // reallocation was not successful, handle as appropriate
}

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

Вам не нужно звонить calloc после звонка realloc. Вам нужно вызвать calloc только один раз, когда вы изначально выделяете буфер. Вы также можете использовать realloc для начального распределения, вы просто передаете NULL в качестве первого параметра:

T *buffer = realloc( NULL, number_of_items * sizeof *buffer );

Изменить

Применяя это к вашему коду:

while ((textInFile[index] = getc(f)) != EOF) {
  if (index > currentLength - 3) {
    ptr = realloc(textInFile, currentLength + 10);
    if ( ptr )
    {
      currentLength += 10;
      textInFile = ptr; 
    }
    else
    {
      // unable to extend textInFile
    }
    ...
        if (k > currentLineLength - 3) {
            ptr = realloc( currentLine, currentLineLength + 10 );
            if ( ptr )
            {
              currentLineLength += 10;
              currentLine = ptr;  
            }
            else
            {
              // unable to extend currentLine
            }
        }

Когда вы закончите, вы будете свободны, как

free( currentLine );
free( textInFile );

Вам не нужно освобождать ptr в любой момент - вы используете его только для хранения временного значения.

person John Bode    schedule 21.11.2019
comment
Мне нужно его использовать, иначе я получу недопустимое чтение размера 1 и недопустимую запись размера один. В строке должно что-то быть. - person tomashauser; 21.11.2019
comment
@tomashauser: Что нужно использовать, вызов calloc? - person John Bode; 22.11.2019
comment
Да, это HW с очень строгим valgrind. - person tomashauser; 23.11.2019