C free(): ongeldige aanwijzer

Ik ben mezelf C aan het aanleren. Mijn doel is om een C-functie te maken die alleen een queryreeks uitvoert en splitst op het ampersand en het isgelijkteken. Ik loop vast op deze fout van Valgrind.

==5411== Invalid free() / delete / delete[] / realloc()
==5411==    at 0x402AC38: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5411==    by 0x804857C: main (leak.c:28)
==5411==  Address 0x420a02a is 2 bytes inside a block of size 8 free'd
==5411==    at 0x402AC38: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5411==    by 0x804857C: main (leak.c:28)
==5411== 
==5411== 
==5411== HEAP SUMMARY:
==5411==     in use at exit: 0 bytes in 0 blocks
==5411==   total heap usage: 1 allocs, 2 frees, 8 bytes allocated
==5411== 
==5411== All heap blocks were freed -- no leaks are possible
==5411== 
==5411== For counts of detected and suppressed errors, rerun with: -v
==5411== ERROR SUMMARY: 20 errors from 9 contexts (suppressed: 0 from 0)

en de backtrace :

*** Error in `./leak': free(): invalid pointer: 0x08c1d00a ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x767c2)[0xb75f17c2]
/lib/i386-linux-gnu/libc.so.6(+0x77510)[0xb75f2510]
./leak[0x804857d]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0xb7594905]
./leak[0x8048421]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:05 262764     /home/danny/dev/c-qs-parser/leak
08049000-0804a000 r--p 00000000 08:05 262764     /home/danny/dev/c-qs-parser/leak
0804a000-0804b000 rw-p 00001000 08:05 262764     /home/danny/dev/c-qs-parser/leak
08c1d000-08c3e000 rw-p 00000000 00:00 0          [heap]
b757a000-b757b000 rw-p 00000000 00:00 0 
b757b000-b7729000 r-xp 00000000 08:05 1312132    /lib/i386-linux-gnu/libc-2.17.so
b7729000-b772b000 r--p 001ae000 08:05 1312132    /lib/i386-linux-gnu/libc-2.17.so
b772b000-b772c000 rw-p 001b0000 08:05 1312132    /lib/i386-linux-gnu/libc-2.17.so
b772c000-b772f000 rw-p 00000000 00:00 0 
b772f000-b774a000 r-xp 00000000 08:05 1312589    /lib/i386-linux-gnu/libgcc_s.so.1
b774a000-b774b000 r--p 0001a000 08:05 1312589    /lib/i386-linux-gnu/libgcc_s.so.1
b774b000-b774c000 rw-p 0001b000 08:05 1312589    /lib/i386-linux-gnu/libgcc_s.so.1
b774c000-b7750000 rw-p 00000000 00:00 0 
b7750000-b7751000 r-xp 00000000 00:00 0          [vdso]
b7751000-b7771000 r-xp 00000000 08:05 1312116    /lib/i386-linux-gnu/ld-2.17.so
b7771000-b7772000 r--p 0001f000 08:05 1312116    /lib/i386-linux-gnu/ld-2.17.so
b7772000-b7773000 rw-p 00020000 08:05 1312116    /lib/i386-linux-gnu/ld-2.17.so
bfe93000-bfeb4000 rw-p 00000000 00:00 0          [stack]
Aborted (core dumped)

Eindelijk hier is de code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
    //char p[] = "t=quote&k=id&v=10";
    char p[] = "t=quote";
    char* token;
    char* tk; 
    char* s;
    unsigned short int found;
    s = strdup(p);
    if (s != NULL) {
        while ((token = strsep(&s, "&")) != NULL) {
            found = 0;
            printf("TOKEN: %s\n\n", token);
            while ((tk = strsep(&token, "=")) != NULL) {
                printf("TK: %s\n\n", tk);
                free(tk);
            }   
            free(token);
        }   
    }   
    free(s);
    return 0;
}

bedankt


Antwoord 1, Autoriteit 100%

Je probeert iets vrij te maken dat geen verwijzing is naar een “vrij te maken” geheugenadres. Het feit dat iets een adres is, betekent niet dat u het moet of moetvrijgeven.

Er zijn twee hoofdtypen geheugen die u verwarrend lijkt: stapelgeheugen en heapgeheugen.

  • Stapelgeheugenleeft in de levensduur van de functie. Het is een tijdelijke ruimte voor dingen die niet te groot mogen worden. Als je de functie mainaanroept, wordt er wat geheugen gereserveerd voor je variabelen die je hebt gedeclareerd (p,token, enzovoort).

  • Heap-geheugen leeft vanaf het moment dat je het malloctot het moment dat je het free. U kunt veel meer heap-geheugen gebruiken dan u geheugen kunt stapelen. Je moet het ook bijhouden – het is niet zo eenvoudig als stapelgeheugen!

Je hebt een paar fouten:

  • Je probeert geheugen vrij te maken dat geen heap-geheugen is. Doe dat niet.

  • Je probeert de binnenkant van een geheugenblok te bevrijden. Als je inderdaad een geheugenblok hebt toegewezen, kun je het alleen vrijmaken van de aanwijzer die wordt geretourneerd door malloc. Dat wil zeggen, alleen vanaf het begin van het blok. Je kunt een deel van het blok niet van binnenuit vrijmaken.

Voor je stukje code hier, wil je waarschijnlijk een manier vinden om een relevant deel van het geheugen naar een andere plek te kopiëren… zeg maar een ander blok geheugen dat je opzij hebt gezet. Of je kunt de originele string wijzigen als je wilt (hint: char-waarde 0 is de nul-terminator en vertelt functies zoals printf om te stoppen met het lezen van de string).

EDIT:De malloc-functie wijst heap-geheugen* toe.

“9.9.1 De malloc en vrije functies

De C-standaardbibliotheek biedt een expliciete allocator die bekend staat als het malloc-pakket. Programma’s wijzen blokken van de heap toe door de malloc-functie aan te roepen.”

~Computer Systems: A Programmer’s Perspective, 2nd Edition, Bryant & O’Hallaron, 2011

EDIT 2:* De C-standaard specificeert in feite niets over de heap of de stapel. Echter, voor iedereen die leert op een relevante desktop/laptop machine, is het onderscheid waarschijnlijk onnodig en verwarrend als er iets is, vooral als je leert hoe je programma wordt opgeslagen en uitgevoerd. Als je merkt dat je werkt aan zoiets als een AVR-microcontroller zoals H2CO3 heeft, is het zeker de moeite waard om alle verschillen op te merken, die uit mijn eigen ervaring met embedded systemen reiken tot ver voorbij de geheugentoewijzing.


Antwoord 2, autoriteit 23%

Waar kwam je op het idee dat je free(token)en free(tk)moet gebruiken? Jij niet. strsep()wijst geen geheugen toe, het retourneert alleen pointers binnen de originele string. Natuurlijk zijn dit geen verwijzingen die zijn toegewezen door malloc()(of iets dergelijks), dus het is ongedefinieerd gedrag om ze free()te geven. Je hoeft alleen free(s)te maken als je klaar bent met de hele string.

Houd er ook rekening mee dat u in uw voorbeeld helemaalgeen dynamische geheugentoewijzing nodig heeft. Je kunt strdup()en free()helemaal vermijden door simpelweg char *s = p;te schrijven.


Antwoord 3, autoriteit 6%

U kunt niet bellen freeop de wijzers die zijn geretourneerd van strsep. Die zijn geen individueel toegewezen snaren, maar alleen aanwijzingen in de string sdie u al hebt toegewezen. Wanneer u klaar bent met sAltogether, moet u het bevrijden, maar u hoeft dat niet te doen met de retourwaarden van strsep.

Other episodes