Verständnisfrage C-Code in VDR

  • Hi!


    Ich habe ein wenig am VDR herumgepatcht und bin dann beim "abkupfern" ;) auf folgenden Code gestoßen. Warum funktioniert das?


    Im oberen Teil wird ein Pointer auf einen Charbuffer gelegt, der ein Duplikat von FileName() ist. Soweit so schön. Der Pointer ist allerdings verschoben um ein paar Byte.
    Dann wird herummanipuliert und zum Schluss der Speicher freigegeben ... aber IMHO nicht der ganze oder?


    Wo ich diesen Code (ohne den Zwischenteil mit strxfrm usw.) verwendet habe, bekomme ich einen Segfault. Warum hier nicht? Übersehe ich was?


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

  • Hi!


    Was meinst du? Wie mein Code ausgesehen hat?
    In etwa so:

    Code
    char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory) + 1));
    printf("%s\n", s);
    free(s);


    Das produzierte Segfaults. Sobald ich aber "free(s)" gegen "free(s - strlen(VideoDirectory) - 1)" getauscht habe hatte ich sie verständlicherweise nicht mehr.


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

  • m
    original VDR:


    s wird nur durchgereicht und nirgenwo verändert und somit sollte free(s) auch ok sein.
    verstehs erstmal auch nicht
    Frithjof

    vdr 1.7.23 suse 12.1 64 Bit 1xTTS2-6400 HD-USB: 24TB
    vdr 1.7.23 suse 11.3 64 Bit 1xTTS2-6400, 1xTTS2-3200 + ci HD:2TB
    vdr 2.2.0 Raspberry pi HD-USB: 2TB (Garten)

  • Hi!


    Aber das Problem (im Fall von VDR und meinem Testcode) müsste ja eigentlich sein dass ich mit strdup einen String kopiere und aber dann einen Pointer auf eine im String befindliche Position speichere. D.h. wenn der String gelöscht wird bleiben ein paar Zeichen im Speicher stehen oder?


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

  • Hi Brougs78,


    der VDR-Code sollte funktionieren. Dort wird ein Pointer
    auf einen Teil des Strings FileName() zurückgeliefert.
    Nur auf diese Kopie wird free() angewendet. Die Kopie
    hat mit dem Original nichts mehr zu tun.


    Mich wundert nur, dass dein Aufruf von

    Code
    free(s - strlen(VideoDirectory) - 1)


    funktioniert, denn genau das müsste einen SEGFAULT liefern.


    Schau nochmal genau nach.


    Viele Grüße
    KdF

  • Hi!


    Wollte das nochmal testen, aber ich habe derzeit das Problem dass ich mit folgendem Code (unter Windows mit mingw und Eclipse) nicht mal einen Segfault provozieren kann:


    Da müssten sich ja jede Menge Speicherlecks rumtreiben da ich free nicht aufrufe ...


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

  • Zitat

    Original von Brougs78
    Da müssten sich ja jede Menge Speicherlecks rumtreiben da ich free nicht aufrufe ...


    Stimmt, aber Du greifst ja nicht auf Speicher zu der nicht Dir gehört, und löscht auch keinen Speicher der nicht Dir gehört. Deshalb gibt's hier verständlicherweise auch keine Segfaults ;)

  • Ein gutes Betriebssystem merkt sich auch den Speicher, den eine Task sich geholt hat und gibt ihn wieder frei, sobald die Task endet, kann aber nicht sagen, ob das bei Windows mit mingw auch so ist.
    In Deinem Beispiel passiert also erstmal nichts schlimmes.
    Falls das Problem noch besteht, dann zeig mal einen größeren Ausschnitt von Deinem Programm.
    Frithjof

    vdr 1.7.23 suse 12.1 64 Bit 1xTTS2-6400 HD-USB: 24TB
    vdr 1.7.23 suse 11.3 64 Bit 1xTTS2-6400, 1xTTS2-3200 + ci HD:2TB
    vdr 2.2.0 Raspberry pi HD-USB: 2TB (Garten)

  • Zitat

    Original von frithjof
    Ein gutes Betriebssystem merkt sich auch den Speicher, den eine Task sich geholt hat und gibt ihn wieder frei, sobald die Task endet, kann aber nicht sagen, ob das bei Windows mit mingw auch so ist.


    Das dürfte grundsätzlich wohl für jedes Protected-Mode Betriebssystem heutzutage gelten. Dennoch ist es eine schlechte Praxis, sich darauf zu verlassen ;) - Denn insbesondere Server und nicht zuletzt ja auch unser VDR werden nicht sehr oft beendet (im Fall VDR jedenfalls nicht bei jedem).

  • Hi!


    Zitat

    Stimmt, aber Du greifst ja nicht auf Speicher zu der nicht Dir gehört, und löscht auch keinen Speicher der nicht Dir gehört. Deshalb gibt's hier verständlicherweise auch keine Segfaults ;)


    ??? also da komme ich jetzt nicht mit. :schiel
    Lt. der Beschreibung des Befehls "strdup()" wird dabei der Speicher für den kopierten String reserviert und man muss ihn selber wieder mit "free()" freigeben. Ist dem nicht so? Oder ist das nur ein Programmierfehler den der Compiler (oder wer auch immer) für mich ausmerzt und mich nicht davor warnt dass ich eigentlich was vergessen habe?


    Kann ich da mit strdup() kein Speicherleck provozieren?


    BTW, wenn ich die Kopie als const char* definiere, dann kann ich free() garnicht darauf anwenden (kompiliert nicht). Gibts da dann nen anderen Befehl oder muss ich da den Speicher nicht freigeben?


    Zitat

    Falls das Problem noch besteht, dann zeig mal einen größeren Ausschnitt von Deinem Programm.


    Also das grundsätzliche Problem habe ich eigentlich nicht mehr, da ich das ganze dann anders gelöst habe und den entsprechenden Code nicht mehr gebraucht habe.
    Allerdings kapier ich das ganze noch nicht so wirklich. (Das soll jetzt übrigens kein "Schlechtmachen" des Codes von VDR sein, sondern ich würde nur gerne kapieren warum das so funktioniert ...anhand des Beispiels VDR)


    In der SortName()-Routine von VDR wird mit strdup() Speicher von n Bytes reserviert und der Pointer zurückgegeben für den Start dieses Speichers. Jetzt wird allerdings dieser Pointer gleich um einige Byte verschoben und der Pointer zeigt nicht mehr an den Start des Speicherbereiches. Wenn jetzt free den Speicher wieder freigibt bei übergabe dieses Pointers, dann - nehme ich an - macht es das von der aktuellen Pointeradresse bis '\0' richtig? Das heißt die paar Byte vor der Pointeradresse die zuerst mitreserviert wurden liegen da immer noch so im Speicher rum (im Beispiel von VDR sowas wie "\video\", oder wie werden die wieder freigegeben?


    Sorry für die Voll-Noob-Fragen, aber irgendwie bin ich wieder auf einem Rückschritt des Verständnisses von C bzw. C++ ... :(


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

  • Zitat

    Original von Brougs78
    ??? also da komme ich jetzt nicht mit. :schiel
    Lt. der Beschreibung des Befehls "strdup()" wird dabei der Speicher für den kopierten String reserviert und man muss ihn selber wieder mit "free()" freigeben. Ist dem nicht so? Oder ist das nur ein Programmierfehler den der Compiler (oder wer auch immer) für mich ausmerzt und mich nicht davor warnt dass ich eigentlich was vergessen habe?


    Ausmerzen tut der Compiler in dem Bereich nichts für Dich. Das Speicherleck entsteht weiterhin. Speicherleck heisst ja, dass Du irgendwo Speicher allokiert hast (hier tut das strdup für Dich), auf den kein Zeiger mehr zeigt (weil der bei jedem Schleifendurchlauf überschrieben wird). Du kommst also später nicht mehr dran, um zu löschen. Nichtsdestotrotz existiert er noch. Aber Speicherlecks verursachen keine Segfaults, verschwenden nur Speicher ;)


    Zitat

    BTW, wenn ich die Kopie als const char* definiere, dann kann ich free() garnicht darauf anwenden (kompiliert nicht). Gibts da dann nen anderen Befehl oder muss ich da den Speicher nicht freigeben?


    Wie das bei free ist kann ich nicht sagen, mit new/delete kann man aber auch konstante Speicherbereiche holen und freigeben. Macht aber imho hier sowieso keinen Sinn, da ja herade der Zweck von strdup ist, Dir eine Kopie zu beschaffen, die Dir gehört, die Du also auch verändern darfst.


    Zitat

    In der SortName()-Routine von VDR wird mit strdup() Speicher von n Bytes reserviert und der Pointer zurückgegeben für den Start dieses Speichers. Jetzt wird allerdings dieser Pointer gleich um einige Byte verschoben und der Pointer zeigt nicht mehr an den Start des Speicherbereiches. Wenn jetzt free den Speicher wieder freigibt bei übergabe dieses Pointers, dann - nehme ich an - macht es das von der aktuellen Pointeradresse bis '\0' richtig? Das heißt die paar Byte vor der Pointeradresse die zuerst mitreserviert wurden liegen da immer noch so im Speicher rum (im Beispiel von VDR sowas wie "\video\", oder wie werden die wieder freigegeben?


    Speicher wird immer blockweise freigegeben, d.h. das System weiss anhand der Adresse, wie groß ein Block ist. Ein Freigeben einer allokierten Adresse + X ist schlichtweg nicht definiert. Allerdings sehe ich in dem angesprochenen Programmteil nicht, wo der Pointer vor dem Freigeben verschoben wird. Dem strdup wird ein Zeiger auf einen Teilstring übergeben (die Addition mit strlen(VideoDirectory) passiert in den Klammern) und er liefert einen neuen Speicherbereich mit einer Kopie dieses Teilstrings zurück. Und der Zeiger, der auf diese Kopie zeigt, wird m.E. in den angegebenen Codes nirgendwo verschoben.

  • Hi!


    Danke für die Erklärung.


    D.h. solange ich mich mit meinem Pointer in dem allokierten Speicher befinde habe ich kein Problem. Wenn ich dann den Pointer an free() übergebe ist es egal ob er gerade am Anfang oder am Ende des Speicherbereichs steht.
    Dann liegt hier wohl mein Denkfehler. Ich dachte free() muss den Anfangsbereich des Speicherbereichs erhalten.


    Wegen dem Beispiel aus VDR: strdup() erstellt IMHO eine vollständige Kopie des Strings und erst dann wird der Zeiger verschoben und "s" verweist auf einen Teilstring. Aber ist ja eigentlich dann egal, wenn free() damit sowieso kein Problem hat.


    Danke nochmal an alle Beteiligten ... wieder ein kleines Rätsel (für mich zumindest :) ) gelöst.


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

  • Zitat

    Original von Brougs78
    D.h. solange ich mich mit meinem Pointer in dem allokierten Speicher befinde habe ich kein Problem. Wenn ich dann den Pointer an free() übergebe ist es egal ob er gerade am Anfang oder am Ende des Speicherbereichs steht.
    Dann liegt hier wohl mein Denkfehler. Ich dachte free() muss den Anfangsbereich des Speicherbereichs erhalten.


    free muss einen Zeiger auf den Anfang erhalten. Ein free in der Mitte ist undefiniert.


    Zitat

    Wegen dem Beispiel aus VDR: strdup() erstellt IMHO eine vollständige Kopie des Strings und erst dann wird der Zeiger verschoben und "s" verweist auf einen Teilstring.


    strdup erstellt eine vollständige Kopie desjenigen Strings, den es übergeben bekommt. Und das ist in diesem Fall ein Teilstring, da die Berechnung in der Klammer schon lange abgeschlossen ist, wenn der Aufruf tatsächlich passiert. Beim Aufruf erhält strdup doch sowieso nur einen Zeiger, es kann also garnicht wissen, um wieviel Stellen Du diesen verschoben haben möchtest.

  • Nicht ganz, der Lord hat schon recht, die Klammern sind wichtig

    Code
    StripEpisodeName(strdup(       FileName() + strlen(VideoDirectory) + 1       ) );
    
    
      /video/Atlantis.......\0


    s Zeigt auf das A von Atlantis und ab da macht strdup() eine Kopie.
    bei free(s) muss s auch noch da stehen.
    man free:
    free() frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is NULL, no operation is performed.
    Frithjof

    vdr 1.7.23 suse 12.1 64 Bit 1xTTS2-6400 HD-USB: 24TB
    vdr 1.7.23 suse 11.3 64 Bit 1xTTS2-6400, 1xTTS2-3200 + ci HD:2TB
    vdr 2.2.0 Raspberry pi HD-USB: 2TB (Garten)

  • Ah sorry ... ich hab' wohl Tomaten auf den Augen. Stimmt natürlich, es wird nur ein Teilstring erstellt.


    Gruß,
    Brougs78

    - -- --- ================================================================ --- -- -
    Antec Fusion, Intel E5200, Asus P5N7A-VM (VDPAU), DD CineS2 v6 + DD DuoFlex CI // yavdr-0.6.1
    - -- --- ================================================================ --- -- -

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!