Вопросы | c

Почему я получаю двойную бесплатную ошибку с помощью realloc ()?

Вопрос

Matthew Schinckel | 6226 просмотров | рейтинг: 3

Я попытался написать функцию замены строки в 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");
    }
}
 



Ответы

John Downey

+ 6 -
Просто выстрел в темноте, потому что я еще не пробовал, но когда вы перераспределяете, он возвращает указатель так же, как malloc. Поскольку realloc может перемещать указатель при необходимости, вы, скорее всего, работаете с недопустимым указателем, если вы не выполните следующие действия:
 input = realloc(input, strlen(input) + delta);
 

 


Mark

+ 4 -
Обратите внимание, попробуйте отредактировать ваш код, чтобы избавиться от escape-кодов html. Ну, хотя с тех пор, как я использовал C / C ++, прошло некоторое время, но растущий realloc использует значение указателя памяти только в том случае, если после исходного блока в памяти есть место. Например, рассмотрим это: (XXXXXXXXXX ..........) Если ваш указатель указывает на первый х, и. означает свободное место в памяти, и вы увеличиваете объем памяти, на который указывает ваша переменная, на 5 байт, это будет успешно. Это, конечно, упрощенный пример, так как блоки округляются до определенного размера для выравнивания, но в любом случае. Однако если впоследствии вы попытаетесь увеличить его еще на 10 байтов, а доступно только 5, потребуется переместить блок в памяти и обновить указатель. Однако, в вашем примере вы передаете функции указатель на символ, а не указатель на вашу переменную, и, таким образом, хотя внутренняя функция strrep может регулировать используемую переменную, она является локальной переменной для функции strrep и Ваш вызывающий код останется с исходным значением переменной указателя. Это значение указателя, однако, было освобождено. В вашем случае вход является виновником. Однако я бы сделал еще одно предложение. В вашем случае, похоже, что входная переменная действительно является входной, и если она есть, ее вообще не следует изменять. Таким образом, я бы попытался найти другой способ сделать то, что вы хотите, без изменения входных данных, поскольку подобные побочные эффекты могут быть трудно отследить.  


Lasse Vågsæther Karlsen

+ 11 -
Как правило, вы никогда не должны делать free или realloc для предоставленного пользователем буфера. Вы не знаете, где пользователь выделил пространство (в вашем модуле, в другой DLL), поэтому вы не можете использовать любую функцию выделения в пользовательском буфере. При условии, что теперь вы не можете выполнять какое-либо перераспределение внутри своей функции, вам следует немного изменить ее поведение, например, сделать только одну замену, чтобы пользователь мог вычислить результирующую максимальную длину строки и предоставить вам буфер, достаточно длинный для этой функции. замена произойдет. Затем вы можете создать другую функцию для выполнения нескольких замен, но вам нужно будет выделить все пространство для полученной строки и скопировать строку ввода пользователя. Затем вы должны предоставить способ удалить выделенную строку. В результате чего:
 void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
 

 


Vincent Robert

+ 0 -
Мои быстрые подсказки. Вместо: void strrep(char *input, char *search, char *replace) пытаться: void strrep(char *input, char *search, char *replace) а чем в теле: input = realloc(input, strlen(input) + delta); Обычно читайте о передаче аргументов функции в виде значений / ссылки и описания realloc () :).  


Jonathan Leffler

+ 3 -
Это похоже на работу;
 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;
}
 

Вздох, есть ли способ опубликовать код без сосания?


John Downey

+ 6 -
Кто-то еще извинился за опоздание на вечеринку - два с половиной месяца назад. О, хорошо, я провожу довольно много времени, занимаясь археологией программного обеспечения. Меня интересует, что никто не прокомментировал явно утечку памяти в оригинальном дизайне или ошибку «один за другим». И именно наблюдение за утечкой памяти говорит мне точно, почему вы получаете ошибку двойного освобождения (потому что, если быть точным, вы освобождаете одну и ту же память несколько раз - и вы делаете это после того, как растоптали уже освобожденную память). Прежде чем проводить анализ, я соглашусь с теми, кто говорит, что ваш интерфейс менее звездный; однако, если вы столкнулись с проблемами утечки / утечки памяти и задокументировали требование «должна быть выделена память», это может быть «ОК». Какие проблемы? Ну, вы передаете буфер функции realloc (), и realloc () возвращает вам новый указатель на область, которую вы должны использовать - и вы игнорируете это возвращаемое значение. Следовательно, realloc (), вероятно, освободил исходную память, а затем вы снова передаете ему тот же указатель, и он жалуется, что вы освобождаете одну и ту же память дважды, потому что вы снова передаете ей исходное значение. Это не только приводит к утечке памяти, но и означает, что вы продолжаете использовать исходное пространство - и выстрел Джона Дауни в темноте указывает на то, что вы неправильно используете realloc (), но не подчеркивает, насколько серьезно вы это делаете. Также есть ошибка «off-by-one», потому что вы не выделяете достаточно места для NUL '/ 0', который завершает строку. Утечка памяти происходит из-за того, что вы не предоставляете механизм, сообщающий вызывающей стороне о последнем значении строки. Поскольку вы продолжали топтать исходную строку и пробел после нее, похоже, что код работал, но если ваш вызывающий код освободил пробел, он также получит ошибку без двойного освобождения, или он может получить дамп ядра или его эквивалент, потому что информация управления памятью полностью зашифрована. Ваш код также не защищает от неопределенного роста - подумайте о замене «Noel» на «Joyeux Noel». Каждый раз вы добавляете 7 символов, но вы найдете другой Ноэль в замененном тексте, расширяете его и так далее, и так далее. Мое исправление (ниже) не решает эту проблему - простое решение, вероятно, состоит в том, чтобы проверить, появляется ли строка поиска в строке замены; альтернатива - пропустить строку замены и продолжить поиск после нее. Второй имеет несколько нетривиальных проблем с кодированием. Итак, моя предлагаемая версия вашей вызываемой функции:
 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 (). См. Книгу Стива Магуайра «Написание твердого кода» для подробного обсуждения вопросов управления памятью.  


Mark

+ 4 -
realloc странный, сложный и должен использоваться только при работе с большим количеством памяти много раз в секунду. то есть - где это на самом деле делает ваш код быстрее. Я видел код где
 realloc(bytes, smallerSize);
 

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


Теги

c | malloc