Warum erhalte ich einen doppelten frei Fehler mit realloc ()?

stimmen
11

Ich habe versucht , eine Zeichenfolge ersetzen Funktion in C zu schreiben, die sich auf ein Werk char *, das unter Verwendung wurde zugewiesen malloc(). Es ist ein wenig anders, dass es Saiten finden und ersetzen, anstatt Zeichen in der Startzeichenfolge.

Es ist trivial zu tun , wenn die Suche und Ersetzen von Strings die gleiche Länge (oder die Zeichenfolge ersetzen ist kürzer als die Suchzeichenfolge), da ich genügend Platz zugewiesen haben. Wenn ich zu verwenden versuchen realloc(), erhalte ich einen Fehler, der mir sagt , ich bin eine doppelte frei machen - was ich sehe nicht , wie ich bin, da ich nur mit realloc().

Vielleicht ein wenig Code helfen:

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

Das Programm funktioniert, bis ich versuche , realloc()in einem Fall , in dem der ersetzt String länger als die ursprüngliche Zeichenfolge sein wird. (Es ist immer noch Art funktioniert, es spuckt nur Fehler aus sowie das Ergebnis).

Wenn es hilft, sieht der Angerufene Code wie:

#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);
    }
}
Veröffentlicht am 04/08/2008 um 12:06
quelle vom benutzer
In anderen Sprachen...                            


8 antworten

stimmen
12

Zunächst einmal, sorry, ich bin spät zur Party. Dies ist meine erste Antwort Stackoverflow. :)

Wie bereits erwähnt, wenn realloc () aufgerufen wird, können Sie möglicherweise den Zeiger auf den Speicher ändern umverteilt werden. Wenn dies geschieht, wird das Argument „string“ ungültig. Auch wenn Sie es neu zuweisen, geht die Änderung des Umfangs, sobald die Funktion beendet.

Um die OP zu beantworten, realloc () gibt einen Zeiger auf die neu umgeschichtet Speicher. Der Rückgabewert muss irgendwo gelagert werden. Im Allgemeinen würden Sie dies:

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

Wie TyBoer weist darauf hin, kann euch nicht der Wert des Zeigers ändern als die Eingabe in dieser Funktion übergeben werden. Sie können zuweisen, was Sie wollen, aber die Änderung außerhalb des Gültigkeitsbereiches am Ende der Funktion gehen. Im folgenden Block „Eingang“ kann oder auch nicht einen ungültigen Zeiger, sobald die Funktion abgeschlossen ist:

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 versucht, durch Rücksendung der neuen Zeiger als die Ausgabe der Funktion, dies zu umgehen. Wenn Sie das tun, ist die Beweislast auf den Anrufer wieder nie den Zeiger er für die Eingabe verwendet verwenden. Wenn es den Rückgabewert übereinstimmt, dann haben Sie zwei Zeiger auf die gleiche Stelle und müssen nur auf einem von ihnen frei () aufrufen. Wenn sie nicht übereinstimmen, weist der Eingabezeiger nun auf Speicher, der kann oder auch nicht durch den Prozess gehören. Dereferencing es könnte einen Segmentierungsfehler verursachen.

Sie könnten einen Doppelzeiger für die Eingabe, wie folgt verwenden:

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

Wenn der Anrufer ein Duplikat des Eingangszeigers irgendwo hat, die noch duplizieren könnte jetzt ungültig.

Ich denke, die sauberste Lösung hier zu vermeiden, ist realloc () verwenden, wenn man versucht die Funktion des Anrufers Eingang zu ändern. Nur malloc () ein neuer Puffer, kehrt das, und lassen Sie den Anrufer entscheiden, ob er den alten Text zu befreien. Dies hat den zusätzlichen Vorteil der Vermietung der Anrufer die ursprüngliche Zeichenfolge halten!

Beantwortet am 08/08/2008 um 22:37
quelle vom benutzer

stimmen
11

In der Regel sollten Sie nie eine freie oder realloc auf einem Benutzer bereitgestellt Puffer tun. Sie wissen nicht , wo der Benutzer den Raum zugewiesen (in Ihrem Modul in einer anderen DLL) , so dass Sie keine der Zuordnungsfunktionen auf einem Benutzerpuffer verwenden können.

Vorausgesetzt, Sie können jetzt eine Umverteilung innerhalb Ihrer Funktion nicht tun, sollten Sie ihr Verhalten ändern sie ein wenig, wie nur einen Ersatz zu tun, so wird der Benutzer in der Lage, die resultierende Zeichenfolge maximale Länge zu berechnen und Sie mit einem Puffer lang genug für diese liefern Ersatz auftreten.

Dann könnten Sie eine andere Funktion erstellen, die mehrere Ersetzungen zu tun, aber Sie werden den ganzen Raum für die resultierende Zeichenfolge zuzuordnen sind und das Benutzereingabestring kopieren. Dann müssen Sie bieten eine Möglichkeit, die Zeichenfolge zugewiesen zu löschen.

Ergebend:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);
Beantwortet am 04/08/2008 um 12:19
quelle vom benutzer

stimmen
6

Jemand anderes entschuldigte sich für spät zur Party zu sein - zweieinhalb Monaten. Na ja, ich verbringe ziemlich viel Zeit Software Archäologie zu tun.

Ich bin daran interessiert, dass niemand explizit auf dem Speicherleck in dem ursprünglichen Entwurf oder den Off-by-one Fehlern kommentiert. Und es war die Beobachtung der Speicherverlust, der mir sagt, genau, warum Sie die Double-Free-Fehler bekommen (da, um genau zu sein, Sie sind mit den gleichen Speicher mehrmals zu befreien - und Sie tun dies nach den bereits freigegebenen Speicher Trampeln über).

Bevor die Analyse leiten, werde ich mit denjenigen überein, die sagen, dass Ihre Schnittstelle ist weniger als stellare; jedoch, wenn Sie mit dem Speicherleck / Trampeln Fragen behandelt und dokumentierten die "zugewiesenen Speicher werden muss Anforderung, könnte es auf‚OK‘sein.

Was sind die Probleme? Nun, passieren Sie einen Puffer realloc () und realloc () gibt Ihnen einen neuen Zeiger auf den Bereich, den Sie verwenden sollen - und Sie ignorieren, dass der Rückgabewert. Folglich realloc () hat wahrscheinlich den ursprünglichen Speicher freigegeben, und dann übergeben Sie es den gleichen Zeiger wieder, und es beschwert sich, dass Sie den gleichen Speicher zweimal sind zu befreien, weil Sie den ursprünglichen Wert wieder passieren. Dadurch wird nicht nur Speicherleck, sondern bedeutet, dass Sie den ursprünglichen Raum verwenden fort - und John Downey Schuss in den dunklen Punkten heraus, dass Sie realloc missbräuchlich (), aber betonen nicht, wie stark macht ihr so. Es gibt auch einen Off-by-one-Fehler, weil Sie für den ‚\ 0‘ NUL nicht genügend Speicherplatz zuweisen, die die Zeichenfolge beendet.

Der Speicherverlust tritt auf, weil Sie den Anrufer über den letzten Wert der Zeichenfolge zu sagen, keinen Mechanismus zur Verfügung stellen. Weil Sie immer die ursprüngliche Zeichenfolge plus den Raum, nachdem er trampelt über, es sieht aus wie der Code funktioniert, aber wenn Ihr Telefonvorwahl den Raum befreit, auch wäre es ein Doppelfreien Fehlermeldung zu erhalten, oder es könnte einen Core-Dump oder gleichwertig, weil die Speichersteuerinformationen vollständig verschlüsselt.

Ihr Code auch nicht zum Schutz gegen unbestimmtes Wachstum - prüfen, ‚Noel‘ mit ‚Joyeux Noel‘ zu ersetzen. Jedes Mal, würden Sie 7 Zeichen hinzufügen, aber Sie würden eine andere Noel im ersetzten Text, und erweitern Sie es, und so weiter und so weiter finden. Mein fixup (unten) nicht mit dieser Frage befassen - die einfache Lösung wahrscheinlich, ob die Such-Zeichenfolge in der Zeichenfolge ersetzen scheint zu prüfen ist; eine Alternative ist, um die Zeichenfolge ersetzen zu überspringen und die Suche nach dem es weiter. Der zweite hat einige nicht-triviale Codierung Probleme zu lösen.

Also, meine vorgeschlagene Überarbeitung Ihrer aufgerufenen Funktion ist:

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

Dieser Code keine Speicherzuordnungsfehler erkennen - und wahrscheinlich abstürzt (aber wenn nicht, verliert Speicher), wenn realloc () fehlschlägt. Siehe Steve Maguire ‚Schreiben Fest-Code‘ Buch für eine umfassende Diskussion über Speicher-Management-Themen.

Beantwortet am 21/10/2008 um 03:41
quelle vom benutzer

stimmen
6

Nur ein Schuss im Dunkeln, weil ich habe es noch nicht ausprobiert, aber wenn Sie realloc es gibt den Zeiger viel wie malloc. Da realloc den Zeiger bewegen kann, wenn nötig Sie höchstwahrscheinlich auf einen ungültigen Zeiger funktionieren, wenn Sie die folgenden nicht tun:

input = realloc(input, strlen(input) + delta);
Beantwortet am 04/08/2008 um 12:14
quelle vom benutzer

stimmen
4

Beachten Sie, versuchen Sie, Ihren Code zu bearbeiten der HTML-Escape-Codes, um loszuwerden.

Nun, obwohl es ist schon eine Weile, da habe ich C / C ++, realloc, die den Speicherzeigerwert nur wieder verwendet wachsen, wenn es Raum im Speicher nach dem ursprünglichen Block ist.

Betrachten wir zum Beispiel diese:

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

Wenn Ihr Zeiger zeigt auf den ersten x und. freien Speicherplatz bedeutet, und Sie wachsen die Speichergröße, indem Sie Ihre Variable um 5 Bytes zeigte, wird es gelingen. Dies ist natürlich ein vereinfachtes Beispiel als Blöcke auf eine bestimmte Größe zur Ausrichtung aufgerundet werden, aber trotzdem.

Allerdings versuchen, wenn Sie es anschließend um weitere 10 Bytes zu wachsen, und es gibt nur 5 vorhanden, müssen sie den Block im Speicher bewegen, und der Zeiger zu aktualisieren.

Sie jedoch die Funktion einen Zeiger auf das Zeichen in Ihrem Beispiel sind vorbei, kein Zeiger auf Ihre Variable und damit während der strrep Funktion intern möglicherweise in der Lage die Variable in Gebrauch einzustellen, es ist eine lokale Variable in der strrep Funktion und Ihr Aufruf Code wird mit dem ursprünglichen Zeigervariable Wert gelassen werden.

Dieser Zeigerwert jedoch freigegeben wurde.

In Ihrem Fall ist Eingabe der Schuldige.

Allerdings würde ich einen anderen Vorschlag machen. In Ihrem Fall sieht es aus wie die Eingangsvariable tatsächlich eingegeben wird, und wenn es ist, sollte es nicht geändert werden, überhaupt nicht .

Ich würde also versuchen , einen anderen Weg zu finden , zu tun , was Sie tun möchten, ohne Änderung Eingang , als Nebenwirkungen wie dies kann schwierig sein , nach unten zu verfolgen.

Beantwortet am 04/08/2008 um 12:17
quelle vom benutzer

stimmen
3

realloc ist seltsam, kompliziert und sollten nur verwendet werden, wenn sie mit viel Speicher viele Male pro Sekunde zu tun. dh - wo es Ihren Code tatsächlich schneller macht.

Ich habe gesehen, wo Code

realloc(bytes, smallerSize);

wurde verwendet, und arbeitete den Puffer, um die Größe, sie kleiner zu machen. Arbeitete über eine Million Mal, dann aus irgendeinem Grunde realloc entschieden, dass selbst wenn Sie die Puffer wurden verkürzt, wäre es Ihnen eine schöne neue Kopie geben. So zum Absturz bringen Sie in einem zufälligen Ort 1/2 Sekunde nach dem schlechten Dinge passiert ist.

Verwenden Sie immer den Rückgabewert von realloc.

Beantwortet am 16/05/2011 um 23:57
quelle vom benutzer

stimmen
3

Dies scheint zu funktionieren;

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

Seufz, ist es trotzdem, Code zu schreiben, ohne es zu saugen?

Beantwortet am 04/08/2008 um 12:39
quelle vom benutzer

stimmen
0

Meine schnelle Hinweise.

Statt:
void strrep(char *input, char *search, char *replace)
versuchen:
void strrep(char *&input, char *search, char *replace)

und als im Körper:
input = realloc(input, strlen(input) + delta);

Im Allgemeinen lesen über Funktionsargumente als Werte / Referenz und realloc () Beschreibung :) vorbei.

Beantwortet am 04/08/2008 um 12:20
quelle vom benutzer

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