Почему я получаю ошибку double free при использовании realloc()?

Я'пытался написать функцию замены строки на C, которая работает с char *, выделенным с помощью malloc(). Она немного отличается тем, что будет находить и заменять строки, а не символы в начальной строке.

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

Возможно, небольшой код поможет:

void strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while (find = strstr(find, search)) {

        if (delta > 0) {
            realloc(input, strlen(input) + delta);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) - (find - input));
        memmove(find, replace, replaceLen);
    }
}

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

Если это поможет, код вызова выглядит следующим образом:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void strrep(char *input, char *search, char *replace);

int main(void) {
    char *input = malloc(81);

    while ((fgets(input, 81, stdin)) != NULL) {
        strrep(input, "Noel", "Christmas");
    }
}
Решение

Как правило, вы никогда не должны выполнять free или realloc для буфера, предоставленного пользователем. Вы не знаете, где пользователь выделил место (в вашем модуле, в другой DLL), поэтому вы не можете использовать ни одну из функций выделения для пользовательского буфера.

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

Затем вы можете создать другую функцию для выполнения нескольких замен, но вам придется выделить все пространство для результирующей строки и скопировать введенную пользователем строку. Затем необходимо предусмотреть способ удаления выделенной строки.

В результате:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Комментарии (0)

Во-первых, жаль, что я'м поздно на вечеринку. Это моя первая ответе на StackOverflow. :)

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

Чтобы ответить на ОП, realloc() возвращает указатель на вновь перераспределить память. Возвращаемое значение нужно где-то хранить. Как правило, вы могли бы сделать это:

data *foo = malloc(SIZE * sizeof(data));
data *bar = realloc(foo, NEWSIZE * sizeof(data));

/* Test bar for safety before blowing away foo */
if (bar != NULL)
{
   foo = bar;
   bar = NULL;
}
else
{
   fprintf(stderr, "Crap. Memory error.\n");
   free(foo);
   exit(-1);
}

Как TyBoer указывает, вы можете't изменить значение указателя, переданного в качестве входных данных для этой функции. Можно назначить что угодно, но выйдет из области видимости в конце функции. В следующем блоке, на "вход" и может или не может быть недопустимый указатель, когда функция завершает:

void foobar(char *input, int newlength)
{
   /* Here, I ignore my own advice to save space. Check your return values! */
   input = realloc(input, newlength * sizeof(char));
}

Марк пытается обойти эту проблему, возвращая новый указатель в качестве результата функции. Если вы сделаете это, ответственность лежит на вызывающей стороны, чтобы никогда больше не использовать указатель, который он использовал для входа. Если он совпадает с возвращаемым значением, то у вас есть два указателя на одном и том же месте и нужно только позвонить бесплатно() на одном из них. Если они Дон'т матч, входной указатель теперь указывает на область памяти, которая может или не может быть владельцем процесса. Разыменование это может вызвать ошибку сегментации.

Можно использовать двойной указатель для ввода, как это:

void foobar(char **input, int newlength)
{
   *input = realloc(*input, newlength * sizeof(char));
}

Если абонент имеет дубликатов входных указатель куда-то, что дубликат еще может быть недействительным.

Я думаю, что наилучшим решением здесь является, чтобы избежать использования realloc() при попытке изменить вызывающая функция'ы входные. Просто malloc() в новый буфер, вернуться, и пусть абонент решает, стоит ли бесплатно старый текст. Это имеет дополнительное преимущество, позволяя абоненту сохранить исходную строку!

Комментарии (0)

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

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

Прежде чем проводить анализ, Я'll не согласен с теми, кто говорит, что ваш интерфейс не столь выдающиеся, однако, если вы имеете дело с утечкой памяти/поправ вопросы и документально 'должно быть выделено памяти' требование, это может быть 'ОК'.

Какие проблемы? Ну, вы передаете буфер realloc () и realloc() возвращает новый указатель на область, которую вы должны использовать - и вы игнорировать возвращаемое значение. Следовательно, realloc (), вероятно, освободил оригинальной памяти, и затем вы передаете это опять тот же указатель, и он жалуется, что вы'вновь освободив два раза одну и ту же память, потому что вы снова проходите первоначальное значение. Это не только утечки памяти, но это означает, что вы продолжаете использовать исходное пространство-и Джон Дауни's в Выстрел в темноте указывает на то, что вы растратите realloc(), но не'т подчеркнуть, насколько сильно вы это делаете. Там'ы также на одну ошибку, потому что вы не выделить достаточно места для нул '\0' это завершает строку.

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

Ваш код также не'т защитить от неограниченного роста-рассмотреть вопрос о замене 'Рождественская' с 'Joyeux Ноэль'. Каждый раз, вы хотели добавить 7 символов, но вы'd не найти другого Ноэль в замене текста, и расширить его, и так далее и тому подобное. Мои исправления (ниже) не решает эту проблему - самое простое решение, вероятно, чтобы проверить, является ли строка поиска отображается в строке замены; в качестве альтернативы можно пропустить строки замены и продолжить поиски. Второй имеет некоторые нетривиальные вопросы кодирования адреса.

Итак, мое предложение по пересмотру вашего вызываемой функции:

char *strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while ((find = strstr(find, search)) != 0) {
        if (delta > 0) {
            input = realloc(input, strlen(input) + delta + 1);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) + 1 - (find - input));
        memmove(find, replace, replaceLen);
    }

    return(input);
}

Этот код не обнаруживает ошибки выделения памяти, и, вероятно, падает (но если нет, утечки памяти), если realloc() завершается неудачей. Стив Магуайр'с 'управления' книги для широкого обсуждения вопросов управления памятью.

Комментарии (1)

Это просто попытка в темноте, потому что я еще не пробовал, но когда вы realloc, он возвращает указатель так же, как malloc. Поскольку realloc может перемещать указатель при необходимости, вы, скорее всего, работаете с недействительным указателем, если не сделаете следующее:

input = realloc(input, strlen(input) + delta);
Комментарии (1)

**Примечание, попробуйте отредактировать ваш код, чтобы избавиться от кодов html.

Хотя я давно не использовал C/C++, realloc, который растет, только повторно использует значение указателя памяти, если есть место в памяти после вашего оригинального блока.

Например, рассмотрим следующее:

(xxxxxxxxxxxx..........)

Если ваш указатель указывает на первый x, а . означает свободную область памяти, и вы увеличите размер памяти, на которую указывает ваша переменная, на 5 байт, то все получится. Конечно, это упрощенный пример, поскольку блоки округляются до определенного размера для выравнивания, но в любом случае.

Однако, если впоследствии вы попытаетесь увеличить его еще на 10 байт, а свободных будет только 5, ему придется переместить блок в памяти и обновить указатель.

Однако в вашем примере вы передаете функции указатель на символ, а не указатель на вашу переменную, и поэтому, хотя внутренняя функция strrep может изменить используемую переменную, она является локальной переменной для функции strrep, и ваш вызывающий код останется с исходным значением переменной-указателя.

Это значение указателя, однако, было освобождено.

В вашем случае виновником является ввод.

Однако я бы сделал еще одно предложение. В вашем случае похоже, что переменная input действительно является входной, а если это так, то ее вообще не следует изменять.

Поэтому я бы попытался найти другой способ сделать то, что вы хотите, не изменяя input, поскольку побочные эффекты, подобные этому, бывает трудно отследить.

Комментарии (0)

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

Я видел код, где

realloc(bytes, smallerSize);

был использован и работал, чтобы изменить размер буфера, делая его меньше. Работал около миллиона раз, потом почему-то realloc решил, что даже если бы Вы были сократить буфер, это даст вам хорошую новую копию. Так вы врежетесь в случайном месте 1/2 секунды после того, как плохое случилось.

Всегда используйте возвращаемое значение realloc.

Комментарии (0)

Это похоже на работу;

char *strrep(char *string, const char *search, const char *replace) {
    char *p = strstr(string, search);

    if (p) {
        int occurrence = p - string;
        int stringlength = strlen(string);
        int searchlength = strlen(search);
        int replacelength = strlen(replace);

        if (replacelength > searchlength) {
            string = (char *) realloc(string, strlen(string) 
                + replacelength - searchlength + 1);
        }

        if (replacelength != searchlength) {
            memmove(string + occurrence + replacelength, 
                        string + occurrence + searchlength, 
                        stringlength - occurrence - searchlength + 1);
        }

        strncpy(string + occurrence, replace, replacelength);
    }

    return string;
}

Вздохнув, все равно там постить код без него сосать?

Комментарии (1)

Мои быстрые подсказки.

Вместо: пустота strrep(типа char ввод, символ поиск, символ замените)` попробуйте: пустота strrep(типа char &вход с char поиск, символ замените)`

и чем в организме: вход = realloc(вход, функция strlen(вход) + Дельта);`

В общем читайте о передача аргументов функции как значения/ссылка и realloc() описание :).

Комментарии (1)