2009-11-14 21 views
35

że masz następujący kod ANSI C, który inicjuje wielowymiarową tablicę:C: Prawidłowo uwalniając pamięć wielowymiarowej tablicy

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

Po użyciu **a, w jaki sposób prawidłowo bezpłatny go z pamięci ?


[Aktualizacja] (Solution)

Dzięki Tim (i innych) answer, mogę teraz zrobić taką funkcję, aby zwolnić pamięć z mojego wielowymiarowej tablicy:

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

Konflikt terminologiczny: to nie jest to, co C zwykle nazywa "tablicą wielowymiarową". Jest to jedyny sposób na użycie składni "a [i] [j]", jednocześnie pozwalając, aby oba wymiary były nieznane w czasie kompilacji. Innym rodzajem tablic wielowymiarowych jest tablica tablic, zamiast tej tablicy wskaźników do (pierwszych elementów) tablic. –

Odpowiedz

59

OK, istnieje uzasadniona dużo zamieszania wyjaśniając dokładnie, co zamówić niezbędne free() połączenia muszą być, więc będę spróbuj wyjaśnić, co ludzie próbują uzyskać i dlaczego.

Zaczynając od podstaw, aby zwolnić pamięć, która została przypisana użyciu malloc(), wystarczy zadzwonić free() z dokładnie wskaźnika które zostały podane przez malloc().Więc na ten kod:

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

trzeba dopasowanie:

free(a); 

i dla tej linii:

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

trzeba dopasowanie:

free(a[i]); 

Wewnątrz podobna pętla.

Tam, gdzie komplikuje się to kolejność, w której to musi się stać. Jeśli kilka razy dzwonisz do malloc(), aby uzyskać kilka różnych porcji pamięci , to w ogóle nie ma znaczenia, którą z nich wywołujesz free(), kiedy wykonałeś z nimi zlecenie. Jednak kolejność jest bardzo ważna z uwagi na konkretny powód: używasz jednego fragmentu z malloc ed pamięci do przechowywania wskazówek do innych części przechowywanych w pamięci malloc. Ponieważ koniecznością nie próba odczytu lub zapisu pamięci kiedy już oddał z free(), oznacza to, że będą musieli uwolnić kawałki z ich wskaźników zapisanych w a[i]przed Ci uwolnić a sam porcja. Poszczególne porcje ze wskazówkami zapisanymi w a[i] nie są zależne od każdego innego , więc mogą być free dw dowolnej kolejności.

więc oddanie to wszystko razem, otrzymujemy w ten sposób:

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

ostatnia wskazówka: podczas wywoływania malloc(), rozważyć zmianę tych:

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

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

do:

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

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

Co to robi? Kompilator wie, że a jest int **, więc może oznaczać, że sizeof(*a) jest taki sam jak sizeof(int *). Jednakże, jeśli później zmienisz zdanie i chcą char s lub short s lub long s lub cokolwiek w swojej tablicy zamiast int S lub dostosować kod do późniejszego stosowania w coś innego, trzeba będzie zmienić tylko ten pozostały odniesienie do int w pierwszej cytowanej linii powyżej, a wszystko inne automatycznie znajdzie się dla ciebie. Spowoduje to usunięcie niezauważonych błędów w przyszłości.

Powodzenia!

+1

+1 Doskonała odpowiedź; dziękuję za wyjaśnienie kwestii dotyczącej "porządku zwrotnego", a także o tym, że robisz 'sizeof (* a)' –

+1

Nie ma za co. Możesz go zaakceptować, gdy będziesz gotowy ... :-) – Tim

+1

Proszę również poprawić mnie, jeśli się mylę, ale czy mam rację mówiąc, że 'sizeof (* a [i])' jest równoważne twoje 'sizeof (* (a [i])), ponieważ notacja tablicowa' [] 'ma wyższy priorytet niż' * '? –

4

Musisz powtórzyć tablicę i zrobić tyle uwolnień, ile mallocs dla wskazanej pamięci, a następnie zwolnić tablicę wskaźników.

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

Cofnij dokładnie to, czego przeznaczono:

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

Pamiętaj, że musisz to zrobić w odwrotnej kolejności z którego pierwotnie przydzielonej pamięci. Jeśli zrobiłbyś najpierw free(a), wtedy a[i] będzie miał dostęp do pamięci po jej uwolnieniu, co jest niezdefiniowanym zachowaniem.

+2

Powiedzenie, że musisz zwolnić w odwrotnej kolejności, może wprowadzać w błąd. Musisz po prostu uwolnić szereg wskaźników po samych wskaźnikach. – Andomar

+0

Czy to nie jest inny sposób na odwrócenie? – GManNickG

+2

Sądzę, że @Andomar oznacza, że ​​nie ma znaczenia, w jakiej kolejności uwolnisz [i], tylko że musisz uwolnić wszystkich przed uwolnieniem. Innymi słowy, możesz uwolnić [0] od [m-1] lub [m-1] przez [0] lub wszystkie nawet [], a następnie kursy. Ale jestem też pewien, że @GregH nie * znaczyło, że musiałeś wykonać polecenie [] w odwrotnej kolejności, szczególnie biorąc pod uwagę jego kod. – paxdiablo

4

Napisz operatorów przydziału w dokładnie odwróconej kolejności, zmieniając nazwy funkcji, a wszystko będzie w porządku.

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

    free(a); 

Oczywiście, nie trzeba deallocate w tej samej kolejności odwróconej. Musisz tylko śledzić uwolnienie tej samej pamięci dokładnie jeden raz i nie "zapomnieć" wskaźników do przydzielonej pamięci (tak jakby to było, gdybyś najpierw uwolnił a). Ale zwolnienie w odwrotnej kolejności jest dobrą rolą kciuka, aby zająć się tą ostatnią.

Jak wskazano przez litb w komentarzach, jeśli przydział/dealokacji miał skutki uboczne (jak new/delete operatorów w C++), czasami do tyłu kolejność dealokacji byłoby ważniejsze niż w tym konkretnym przykładzie.

+0

Dlaczego musisz odwrócić kolejność w pętli? –

+0

Ponieważ [1] zostało przydzielone po [0], należy najpierw zwolnić [1]. –

+0

Dlaczego musisz zwolnić [1] przed [0]? Są to różne kawałki malloced, nie ma zależności między nimi –

1

nazwałbym malloc() i free() tylko raz:

#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

jak to jest odpowiedź na pytanie? – Blindy

+0

Pavel Shved napisał poprawną odpowiedź. Właśnie napisałem komentarz z jakimś kodem. – sambowry

+1

Powinieneś pisać komentarze w polu "komentarz" pytania. Obsługuje również bloki kodu. –