Dlaczego otrzymuję podwójne bezpłatne błąd z realloc ()?

głosy
11

Próbowałem napisać ciąg zastąpić funkcję w C, który działa na zasadzie char *, która została przydzielona użyciu malloc(). To trochę różni się tym, że znajdzie i zastąpić ciągi znaków, zamiast w ciągu wyjściowej.

To trywialne zrobić, jeśli wyszukiwania i zamiany ciągów są tej samej długości (lub zastąpić ciąg jest krótszy niż ciąg wyszukiwania), ponieważ mam za mało miejsca przydzielone. Jeśli próbuję użyć realloc(), pojawia się błąd, który mówi mi, że robię podwójne wolne - co nie widzę, jaki jestem, bo jestem tylko przy użyciu realloc().

Może trochę kod pomoże:

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

Program działa, dopóki nie próbują realloc()w przypadku, gdy zastępuje ciąg będzie dłuższy niż początkowy ciąg. (Jest jeszcze niby działa, to po prostu wypluwa błędy, jak również wyniku).

Jeśli to nie pomaga, kod wywołujący wygląda następująco:

#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);
    }
}
Utwórz 04/08/2008 o 12:06
źródło użytkownik
W innych językach...                            


8 odpowiedzi

głosy
12

Po pierwsze, przepraszam za spóźnienie do partii. To jest mój pierwszy stackoverflow odpowiedź. :)

Jak już wspomniano, gdy realloc () jest wywoływana, można potencjalnie zmienić wskaźnik do pamięci jest przesunięte. Kiedy to nastąpi, argument „string” staje się nieważny. Nawet jeśli go przypisać, zmiana wykracza poza zakres raz funkcja kończy.

Aby odpowiedzieć na OP, realloc () zwraca wskaźnik do nowo przeniesioną pamięci. Zwracana wartość musi być przechowywane gdzieś. Ogólnie rzecz biorąc, byłoby to zrobić:

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

Jak TyBoer zaznacza, wy nie może zmienić wartość wskaźnika jest przekazywana jako wejście do tej funkcji. Można przypisać cokolwiek chcesz, ale zmiana wyjdzie z zakresu pod koniec funkcji. W następnym bloku „Wejście” może lub nie może być nieprawidłowy wskaźnik raz funkcja uzupełnia:

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

Mark stara się to obejść przez powrót nowego wskaźnika, jak wyjście z funkcji. Jeśli tak, że spoczywa na rozmówcy, aby nigdy ponownie wykorzystać wskaźnik on używany do wejścia. Jeśli pasuje zwracanej wartości, to masz dwa wskaźniki w tym samym miejscu i tylko trzeba zadzwonić free () na jednym z nich. Jeśli nie pasuje, wskaźnik wskazuje teraz wejście do pamięci, które mogą lub nie mogą być w posiadaniu procesu. Wyłuskania go może spowodować usterkę segmentacji.

Można użyć podwójnego wskaźnika dla wejścia, jak poniżej:

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

Jeśli rozmówca ma duplikat wskaźnik wejścia gdzieś, że duplikat nadal może być nieważny teraz.

Myślę, że tutaj jest najczystszym rozwiązanie, aby uniknąć używania realloc (), gdy próbuje modyfikować dane wejściowe funkcyjnego rozmówcy. Wystarczy malloc () nowy bufor, że powróci i niech rozmówca zdecydować czy uwolnić stary tekst. Ma to tę dodatkową zaletę pozwalając rozmówcy zachować oryginalny łańcuch!

Odpowiedział 08/08/2008 o 22:37
źródło użytkownik

głosy
11

Zgodnie z ogólną zasadą, należy nigdy robić wolną lub realloc na buforze warunkiem użytkownika. Nie wiem, gdzie użytkownik przydzielona przestrzeń (w module, w innym DLL), więc nie można użyć dowolnej funkcji alokacji na bufor użytkownika.

Pod warunkiem, że teraz nie może wykonywać żadnej realokacji wewnątrz swojej funkcji, należy zmienić jego zachowanie trochę, jak robi tylko jedną wymianę, dzięki czemu użytkownik będzie mógł obliczyć wynikającą długości ciąg max i dostarczy Ci z buforem wystarczająco długo na ten jeden wymiana nastąpi.

Następnie można utworzyć inną funkcję na robienie wielu zamienników, ale trzeba będzie przeznaczyć całą przestrzeń dla powstałego łańcucha i skopiować ciąg wejściowy użytkownika. Następnie należy podać sposób, aby usunąć ciąg przydzielone.

W wyniku:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Odpowiedział 04/08/2008 o 12:19
źródło użytkownik

głosy
6

Ktoś przeprosił za spóźnienie do partii - dwa i pół miesiąca temu. No cóż, spędzam sporo czasu robi oprogramowanie archeologię.

Jestem zainteresowany, że nikt nie skomentował wyraźnie o przeciek pamięci w pierwotnym projekcie lub błędu off-by-one. I to był obserwując przeciek pamięci, który mówi mi dokładnie dlaczego otrzymujesz podwójne wolne błąd (bo, być precyzyjne, jesteś uwalniając samej pamięci wiele razy - i robisz to po deptanie nad już zwolniona pamięci).

Przed przeprowadzeniem analizy, ja zgadzam się z tymi, którzy mówią, Twój interfejs jest mniej niż gwiezdne; Jeśli jednak do czynienia z wyciekiem pamięci / zagadnień deptanie i udokumentowany wymóg „musi zostać przydzielona pamięć”, to może być „OK”.

Jakie są problemy? Cóż, można przejść do bufora realloc () i realloc () zwraca ci nowy wskaźnik do obszaru należy wykorzystać - i zignorować tę wartość powrotną. W konsekwencji, realloc () prawdopodobnie uwolniony oryginalną pamięć, a następnie przekazać je ten sam wskaźnik ponownie i narzeka, że ​​masz taką samą pamięć uwalniając dwa razy, ponieważ przechodzą oryginalną wartość do niego ponownie. To nie tylko przecieki pamięci, ale oznacza to, że w dalszym ciągu używać oryginalnej przestrzeni - John Downey strzał w ciemnych punktów na które nadużywają realloc (), ale nie podkreślić, jak poważnie robisz tak. Jest też błąd off-by-one, ponieważ nie przeznaczyć wystarczającą przestrzeń dla nul „\ 0”, który kończy ciąg.

Przeciek pamięci występuje, ponieważ nie zapewniają mechanizm do opowiedzenia rozmówcę o ostatniej wartości łańcucha. Ponieważ przechowywane deptanie przez oryginalny łańcuch plus miejsca po nim, wygląda na to, kod działa, ale jeśli Twój kod wywołujący uwalnia przestrzeń, to też dostanie podwójne wolne błąd, lub może uzyskać zrzut rdzenia lub równoważne, ponieważ informacja o sterowaniu pamięci jest całkowicie zakodowany.

Twój kod również nie chroni przed nieokreślony wzrostu - rozważyć zastąpienie Noel „” z „Joyeux Noel”. Za każdym razem, należy dodać 7 znaków, ale trzeba znaleźć inny Noel w zastąpiony tekstem i go rozwinąć, i tak dalej i tak dalej. Moja fixup (poniżej) nie rozwiązuje ten problem - proste rozwiązanie jest chyba, aby sprawdzić, czy ciąg wyszukiwania pojawia się w łańcuchu wymiany; alternatywą jest przeskoczyć ciąg zastąpić i kontynuować poszukiwania po niej. Drugi ma pewne nietrywialne problemy kodowania do rozwiązania.

Więc moja sugerowana zmiana swojej nazwie funkcji jest następująca:

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

Kod ten nie wykrywa błędy alokacji pamięci - i prawdopodobnie zawiesza (ale jeśli nie, to przecieki pamięci) czy realloc () zakończy się niepowodzeniem. Patrz „Pisanie kodu” lita książkę Steve Maguire dla szerokiej dyskusji na temat kwestii związanych z zarządzaniem pamięcią.

Odpowiedział 21/10/2008 o 03:41
źródło użytkownik

głosy
6

Po prostu strzał w ciemno, bo nie próbowałem jeszcze, ale kiedy realloc zwraca wskaźnik dużo jak malloc. Ponieważ realloc może przesunąć wskaźnik, czy potrzebne Ci są najprawdopodobniej działa na nieprawidłowym wskaźnikiem jeśli nie wykonać następujące czynności:

input = realloc(input, strlen(input) + delta);
Odpowiedział 04/08/2008 o 12:14
źródło użytkownik

głosy
4

Uwaga, spróbuj zmienić swój kod, aby pozbyć kodów html ewakuacyjnych.

Dobrze, choć to było dawno użyłem C / C ++, realloc, które rośnie ponownie wykorzystuje jedynie wartość wskaźnika pamięci, jeśli nie ma miejsca w pamięci po oryginalnego bloku.

Na przykład, rozważ to:

(Xxxxxxxxxx ..........)

Jeśli twoje punkty wskaźnik do pierwszego X i. Oznacza wolnego miejsca w pamięci, a rośnie wielkość pamięci wskazywanego przez zmienną o 5 bajtów, to będzie sukces. Oczywiście, jest to uproszczony przykład jako bloki są zaokrąglone do określonej wielkości, do osiowania, ale w każdym razie.

Jednakże, jeśli następnie starają się rozwijać go przez kolejne 10 bajtów, a tam jest tylko 5 dostępny będzie trzeba przenieść blok w pamięci i aktualizować wskaźnik.

Jednak w swoim przykładem są przechodzącą obok funkcji wskaźnik do znaku, nie wskaźnik do zmiennej, a więc gdy funkcja strrep wewnętrznie może być w stanie ustawić zmienną w użyciu, jest to zmienna lokalna w funkcji strrep i Twój kod nazywając zostaną pozostawione z oryginalnego wskaźnika wartości zmiennej.

Ta wartość wskaźnika, jednak została zwolniona.

W twoim przypadku, wejście jest winowajcą.

Chciałbym jednak dokonać innego sugestię. W twoim przypadku wygląda na to wejście zmienna jest rzeczywiście wejście, a jeśli jest, to nie powinny być zmieniane, w ogóle.

Chciałbym więc spróbować znaleźć inną drogę do tego, co chcesz zrobić, bez zmiany wejścia , jako skutki uboczne, jak to może być trudne do wyśledzenia.

Odpowiedział 04/08/2008 o 12:17
źródło użytkownik

głosy
3

realloc jest dziwne, skomplikowane i powinny być stosowane tylko wtedy, gdy do czynienia z dużą ilością partii pamięci razy na sekundę. czyli - gdzie faktycznie sprawia, że ​​kod szybciej.

Widziałem kodu, w którym

realloc(bytes, smallerSize);

był używany i pracował, aby zmienić rozmiar bufora, dzięki czemu jest mniejsze. Pracował około milion razy, a potem z jakiegoś powodu realloc zdecydował, że nawet jeśli były skrócenie bufor, to daje ładny nowy egzemplarz. Więc awarię w losowej kolejności 1/2 sekundy po złych rzeczy stało.

Zawsze należy używać wartości zwracanej realloc.

Odpowiedział 16/05/2011 o 23:57
źródło użytkownik

głosy
3

To wydaje się działać;

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

Ech, czy jest tak, aby umieścić kod to bez ssania?

Odpowiedział 04/08/2008 o 12:39
źródło użytkownik

głosy
0

Moi Szybkie wskazówki.

Zamiast:
void strrep(char *input, char *search, char *replace)
try:
void strrep(char *&input, char *search, char *replace)

i niż w organizmie:
input = realloc(input, strlen(input) + delta);

Generalnie czytać o przekazywanie argumentów funkcji jako wartości odniesienia i / realloc () OPIS :).

Odpowiedział 04/08/2008 o 12:20
źródło użytkownik

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