2012-06-17 8 views
63

Sto cercando di imparare C e attualmente sto provando a scrivere una struttura di base dei dati dello stack, ma non riesco a ottenere il minimo malloc/free.Memoria libera allocata in una funzione diversa?

Ecco il codice che ho usato (sto solo postando un piccola parte qui per illustrare un problema specifico, non il codice totale, ma il messaggio di errore è stato generato semplicemente eseguendo questo codice di esempio nel valgrind)

#include <stdio.h> 
#include <stdlib.h> 

typedef struct Entry { 
    struct Entry *previous; 
    int value; 
} Entry; 

void destroyEntry(Entry entry); 

int main(int argc, char *argv[]) 
{ 
    Entry* apple; 
    apple = malloc(sizeof(Entry)); 
    destroyEntry(*(apple)); 
    return 0; 
} 

void destroyEntry(Entry entry) 
{ 
    Entry *entry_ptr = &entry; 
    free(entry_ptr); 
    return; 
} 

Quando corro attraverso valgrind con --leak-check=full --track-origins=yes, ottengo il seguente errore:

==20674== Invalid free()/delete/delete[]/realloc() 
==20674== at 0x4028E58: free (vg_replace_malloc.c:427) 
==20674== by 0x80485B2: destroyEntry (testing.c:53) 
==20674== by 0x8048477: main (testing.c:26) 
==20674== Address 0xbecc0070 is on thread 1's stack 

credo questo errore significa che la funzione destroyEntry non è consentito di modificare la memoria al localizzato esplicitamente in main. È giusto? Perché non posso semplicemente aggiungere la memoria che ho assegnato in main alla funzione free in un'altra funzione? (e questo comportamento è in qualche modo specifico per il main?)

+17

+1 per domande chiare e SSCCE. –

+7

@MatteoItalia Non avevo mai sentito parlare di [SSCCE] (http://sscce.org/) prima. Sicuramente un buon concetto. Grazie per avermi fatto conoscere. –

+0

Chiama per valore ??? –

risposta

50

Ogni volta che si passa un parametro a una funzione, viene eseguita una copia e la funzione funziona su quella copia. Quindi, nel tuo caso, stai provando a free una copia dell'oggetto originale, che non ha alcun senso.

È necessario modificare la funzione per prendere un puntatore e quindi è possibile farlo chiamare free direttamente su quel puntatore.

36

Questo valore viene passato per valore, il che significa che la copia viene creata, quindi si tenta di liberare la memoria, dove risiede la variabile locale entry. Si noti che entry è un oggetto con durata di archiviazione automatica e la memoria in cui risiede verrà liberata automaticamente quando il programma non rientra nell'ambito della funzione destroyEntry.

void destroyEntry(Entry entry) 
{ 
    Entry *entry_ptr = &entry; 
    free(entry_ptr); 
    return; 
} 

La funzione dovrebbe prendere un puntatore (il passaggio per riferimento):

void destroyEntry(Entry *entry) 
{ 
    free(entry); 
} 

Poi invece di destroyEntry(*(apple)); basta chiamare destroyEntry(apple);. Notare che se non ci sono altre funzionalità connesse alla funzione destroyEntry, è ridondante ed è preferibile chiamare direttamente free(apple).

9

Le altre risposte indicano il problema principale: poiché si denitera la mela quando si chiama destroyEntry in main(), passa per riferimento, creando una copia.

Anche se si conosce il problema, è utile tornare all'errore e provare a connettere il testo di ciò che si sta verificando al problema, in modo che la prossima volta che si verifichi sia più probabile che lo si immagini fuori rapidamente. Trovo che gli errori C e C++ possano sembrare talvolta ambiguamente esasperanti.

In genere, quando ho problemi a liberare i puntatori o eliminare gli oggetti, mi piace stampare gli indirizzi, soprattutto quando li assegno e proprio quando provo a liberarlo. valgrind ti ha già dato l'indirizzo del puntatore cattivo, ma aiuta a confrontarlo con un buon indicatore.

int main() 
{ 
    Entry * apple; 
    apple = malloc(sizeof(Entry)); 
    printf("apple's address = %p", apple); // Prints the address of 'apple' 
    free(apple); // You know this will work 
} 

Dopo aver fatto questo, ci si accorge di printf() ha fornito un indirizzo simile 0x8024712 (solo che compongono un indirizzo nella gamma generale a destra), ma l'output valgrind dato 0x4028E58. Noteresti che si trovano in due posti molto diversi (infatti, "0x4 ..."è in pila, non l'heap da cui malloc() assegna, ma presumo che se stai iniziando a dire che non è ancora una bandiera rossa per te), quindi sai che stai cercando di liberare memoria dal posto sbagliato, quindi "invalid free()".

Quindi da lì puoi dire a te stesso "Ok, in qualche modo il mio puntatore si sta corrompendo." Hai già ridotto il tuo problema a un piccolo esempio compilabile, così ha vinto Da lì impieghi molto per risolverlo.

TL; DR - quando si eseguono errori relativi ai puntatori, prova a stampare gli indirizzi o trovarli nel debugger preferito. Spesso ti indirizza almeno nella giusta direzione

Niente di tutto questo serve a scoraggiare la pubblicazione della tua domanda su Stack Exchange, ovviamente. Centinaia di programmatori probabilmente trarranno vantaggio dal fatto che tu l'abbia fatto.

+0

Grazie per aver postato questo ... è davvero un bel punto per capire gli errori con i puntatori e così via. Ho imparato a conoscere stack vs heap, ma non mi sono reso conto che avevano indirizzi di memoria generalizzati. (quindi, 'NULL' ->' 0x0', 'stack' ->' 0x4' e heap è nient'altro?). –

+0

Inoltre, non posso apportare una modifica di un carattere al tuo post, ma nella stringa printf: '" indirizzo di apple =% d "' la stringa di formato dovrebbe essere veramente '% p', non'% d', giusto? –

+0

Non posso dirti con certezza al 100% che gli indirizzi heap saranno sempre '0x8 ...' e gli indirizzi stack saranno sempre '0x4 ...'. Per essere onesti, in questi giorni sono in gran parte integrati nella programmazione in cui lo spazio degli indirizzi è molto ben definito e il tuo processo è l'unico in esecuzione. Tuttavia, la mia comprensione è che su x86, ogni processo ottiene il proprio spazio di indirizzamento virtuale e nella mia esperienza quelle "regole" sembrano essere vere. Grazie per aver segnalato il problema% d. Funziona (nel senso che stampa l'indirizzo), ma è più difficile da leggere. Io di solito uso "0x% x" me stesso perché è così che sono abituato a vederlo. – gkimsey