2009-11-14 7 views
35

Diciamo che avete il seguente codice ANSI C che inizializza un array multi-dimensionale:C: correttamente liberando la memoria di un array multi-dimensionale

int main() 
{ 
     int i, m = 5, n = 20; 
     int **a = malloc(m * sizeof(int *)); 

     //Initialize the arrays 
     for (i = 0; i < m; i++) { 
      a[i]=malloc(n * sizeof(int)); 
     } 

     //...do something with arrays 

     //How do I free the **a ? 

     return 0; 
} 

Dopo aver utilizzato il **a, come faccio correttamente libera dalla memoria ?


[Update] (Soluzione)

Grazie a Tim (e gli altri) answer, ora posso fare una funzione per liberare la memoria del mio array multi-dimensionale:

void freeArray(int **a, int m) { 
    int i; 
    for (i = 0; i < m; ++i) { 
     free(a[i]); 
    } 
    free(a); 
} 
+2

Cavillo terminologico: questo non è ciò che C solitamente chiama "array multidimensionale". È solo l'unico modo per usare la sintassi 'a [i] [j]', pur consentendo a entrambe le dimensioni di essere sconosciute in fase di compilazione. L'altro tipo di matrice multidimensionale è una matrice di matrici, invece di questa matrice di puntatori a (i primi elementi di) matrici. –

risposta

59

OK, c'è una buona dose di confusione spiegare esattamente che cosa ordinare le necessarie free() le chiamate devono essere in, quindi mi prova a chiarire che cosa cercano le persone e perché.

Partendo dalle basi, per liberare la memoria che è stata assegnata utilizzando malloc(), è sufficiente chiamare free() esattamente il puntatore che ti sono stati forniti da malloc().Quindi, per questo codice:

int **a = malloc(m * sizeof(int *)); 

hai bisogno di una corrispondenza:

free(a); 

e per questa linea:

a[i]=malloc(n * sizeof(int)); 

hai bisogno di una corrispondenza:

free(a[i]); 

all'interno di un ciclo simile.

Dove questo diventa complicato è l'ordine in cui ciò deve accadere. Se chiami più volte malloc() per ottenere diversi pezzi della memoria , in generale non importa quale ordine chiami free() quando hai finito con lo . Tuttavia, l'ordine è importante qui per un motivo specifico molto : si sta utilizzando una porzione di memoria malloc per contenere i puntatori ad altri blocchi di memoria malloc ed. Perché si must non tentativo di lettura o scrittura della memoria una volta che avete lo restituì con free(), ciò significa che si sta andando ad avere per liberare i pezzi con loro puntatori memorizzati nel a[i]prima si libera la a stesso blocco. I singoli blocchi con puntatori memorizzati in a[i] non dipendono da ciascun altro, e quindi possono essere free d nell'ordine desiderato.

Così, mettendo insieme tutto questo, otteniamo questo:

for (i = 0; i < m; i++) { 
    free(a[i]); 
} 
free(a); 

Un ultimo consiglio: quando si chiama malloc(), pensare di cambiare questi:

int **a = malloc(m * sizeof(int *)); 

a[i]=malloc(n * sizeof(int)); 

a:

int **a = malloc(m * sizeof(*a)); 

a[i]=malloc(n * sizeof(*(a[i]))); 

Cosa sta facendo? Il compilatore sa che a è un int **, quindi può determinare che sizeof(*a) è lo stesso di sizeof(int *). Tuttavia, se in seguito si cambia idea e vuole char s o short s o long s o qualunque sia nella propria matrice, invece di int s, o si adattano questo codice per dopo uso in qualcosa d'altro, si dovrà cambiare solo quello rimanente fa riferimento a int nella prima riga sopra citata, e tutto il resto si posizionerà automaticamente per te. Ciò rimuove la probabilità di errori non notificati in futuro.

Buona fortuna!

+1

+1 Risposta eccellente; grazie per aver spiegato il problema relativo all'ordine di'verso 'e anche il punto sul fare 'sizeof (* a)' –

+1

Prego. Sentiti libero di accettarlo quando sei pronto .... :-) – Tim

+1

Inoltre, correggimi se sbaglio, ma dovrei essere corretto nel dire che 'sizeof (* a [i])' è equivalente a il tuo 'sizeof (* (a [i]))', poiché la notazione dell'array '[]' ha una precedenza più alta di '*'? –

4

È necessario ripetere di nuovo l'array e fare quanti più liberi mallocs per la memoria puntata, quindi liberare l'array di puntatori.

for (i = 0; i < m; i++) { 
     free (a[i]); 
} 
free (a); 
8

Annulla esattamente ciò che si stanziati:

for (i = 0; i < m; i++) { 
     free(a[i]); 
    } 
    free(a); 

Si noti che è necessario farlo nel inverso ordine da cui è stato originariamente assegnato la memoria. Se hai fatto prima lo free(a), allora a[i] accederà alla memoria dopo che è stata liberata, il che è un comportamento indefinito.

+2

Dire che devi liberare in ordine inverso potrebbe essere fuorviante. Devi solo liberare la serie di puntatori dopo i puntatori stessi. – Andomar

+0

Non è un altro modo di dire contrario? – GManNickG

+2

Penso che @Andomar significhi che non importa in quale ordine si libera l'a [i], solo che devi liberarli tutti prima di liberare un. In altre parole, puoi liberare un [0] attraverso un [m-1] o un [m-1] attraverso un [0] o tutti i pari a [] seguiti dalle probabilità. Ma sono anche sicuro che @GregH non * significa * che dovevi fare i [] in ordine inverso, specialmente dato il suo codice. – paxdiablo

4

Scrivi gli operatori di allocazione nell'ordine esattamente inverso, modificando i nomi delle funzioni e tutto andrà bene.

//Free the arrays 
    for (i = m-1; i >= 0; i--) { 
     free(a[i]); 
    } 

    free(a); 

Naturalmente, non c'è bisogno di deallocare nella stessa ordine inverso. Devi solo tenere traccia di liberare la stessa memoria esattamente una volta e non "dimenticare" i puntatori alla memoria allocata (come sarebbe stato se prima avessi liberato lo a). Ma riassegnare in ordine inverso è un buon ruolo del pollice per affrontare quest'ultimo.

Come evidenziato dai litb nei commenti, se l'allocazione/deallocazione avuto effetti collaterali (come new/delete operatori C++), a volte l'ordine inverso di deallocazione sarebbe più importante che in questo particolare esempio.

+0

Perché devi invertire l'ordine nel ciclo? –

+0

Poiché un [1] è stato assegnato dopo un [0], quindi è necessario deallocare prima un [1]. –

+0

Perché è necessario liberare un [1] prima di [0]? Sono diversi pezzi malleati, non c'è dipendenza tra loro –

1

che chiamerei malloc() e free() una sola volta:

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

int main(void){ 
    int i, m = 5, n = 20; 
    int **a = malloc(m*(sizeof(int*) + n*sizeof(int))); 

    //Initialize the arrays 
    for(a[0]=(int*)a+m, i=1; i<m; i++) a[i]=a[i-1]+n; 

    //...do something with arrays 

    //How do I free the **a ? 
    free(a); 

    return 0; 
}
+0

come è questa una risposta alla domanda? – Blindy

+0

Pavel Shved ha scritto la risposta corretta. Ho appena scritto un commento con un po 'di codice. – sambowry

+1

Dovresti scrivere commenti nel campo "commento" della domanda. Supporta anche i blocchi di codice. –