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

голоса
11

Я пытался написать строку заменить функцию в 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);
    }
}
Задан 04/08/2008 в 12:06
источник пользователем
На других языках...                            


8 ответов

голоса
12

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

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

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

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 указывает, вы, ребята, не может изменить значение указателя передается в качестве входных данных для этой функции. Вы можете назначить все, что вы хотите, но изменения будут выходить за рамки в конце функции. В следующем блоке «вход» может или не может быть неверным указатель когда функция завершается:

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));
}

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

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

Ответил 08/08/2008 в 22:37
источник пользователем

голоса
11

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

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

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

В результате чего:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Ответил 04/08/2008 в 12:19
источник пользователем

голоса
6

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

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

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

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

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

Ваш код также не защищает от бессрочного роста - рассмотреть вопрос о замене «Noel» с «Счастливого Рождества». Каждый раз, когда вы добавить 7 символов, но вы бы найти другой Noel в заменяющем тексте, а также расширить его, и так далее, и так далее. Мой Fixup (ниже) не решает эту проблему - простое решение, вероятно, чтобы проверить, отображается ли строка поиска в строке замены; альтернатива перескочить на замену строки и продолжить поиск после него. Второй имеют некоторые нетривиальные вопросы кодирования для решения.

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

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);
}

Этот код не обнаруживает ошибки выделения памяти - и, вероятно, выходит из строя (а если нет, утечки памяти), если перераспределить () терпит неудачу. См «Ввод кода» Solid книга Стива Maguire для широкого обсуждения вопросов управления памятью.

Ответил 21/10/2008 в 03:41
источник пользователем

голоса
6

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

input = realloc(input, strlen(input) + delta);
Ответил 04/08/2008 в 12:14
источник пользователем

голоса
4

Обратите внимание, попытайтесь изменить код, чтобы избавиться от кодов HTML побега.

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

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

(XXXXXXXXXX ..........)

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

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

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

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

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

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

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

Ответил 04/08/2008 в 12:17
источник пользователем

голоса
3

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

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

realloc(bytes, smallerSize);

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

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

Ответил 16/05/2011 в 23:57
источник пользователем

голоса
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;
}

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

Ответил 04/08/2008 в 12:39
источник пользователем

голоса
0

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

Вместо того , чтобы :
void strrep(char *input, char *search, char *replace)
попробовать:
void strrep(char *&input, char *search, char *replace)

и чем в организме:
input = realloc(input, strlen(input) + delta);

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

Ответил 04/08/2008 в 12:20
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more