C: prawidłowe uwolnienie pamięci wielowymiarowej tablicy
Załóżmy, że masz następujący kod ANSI C, który inicjalizuje tablicę wielowymiarową:
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
, Jak poprawnie uwolnić go z pamięci ?
[Update] (Solution)
Dzięki odpowiedzi Tima (i innych) , mogę teraz wykonać taką funkcję, aby zwolnić pamięć z mojej tablicy wielowymiarowej:
void freeArray(int **a, int m) {
int i;
for (i = 0; i < m; ++i) {
free(a[i]);
}
free(a);
}
5 answers
OK, jest sporo zamieszania wyjaśniającego dokładnie, jaka kolejność konieczne połączenia muszą być w, więc postaram się wyjaśnić, co ludzie próbują dostać się do i dlaczego.
Zaczynając od podstaw, aby zwolnić pamięć, która została przydzielona
używając malloc()
, po prostu wywołujesz free()
dokładnie ze wskaźnikiem
które otrzymałeś od malloc()
. Więc dla tego kodu:
int **a = malloc(m * sizeof(int *));
Potrzebujesz dopasowania:
free(a);
I dla tej linii:
a[i]=malloc(n * sizeof(int));
Potrzebujesz dopasowanie:
free(a[i]);
Wewnątrz podobnej pętli.
Gdzie to się komplikuje, jest kolejność, w jakiej to musi się wydarzyć. Jeśli
dzwonisz malloc()
kilka razy, aby uzyskać kilka różnych kawałków
pamięci, w ogóle nie ma znaczenia, jaką kolejność nazwiesz free()
, gdy
skończyłeś z nimi. Jednak porządek jest tu ważny dla bardzo
konkretny powód: używasz jednego kawałka malloc
pamięci ed do przechowywania
wskaźniki do innych fragmentów malloc
pamięci ed. Bo ty musi
Nie spróbuj odczytać lub zapisać pamięć po oddaniu jej z
free()
, oznacza to, że będziesz musiał uwolnić kawałki z
ich wskaźniki przechowywane w a[i]
przed uwolnisz a
sam kawałek.
Poszczególne fragmenty ze wskaźnikami przechowywane w a[i]
nie zależą od każdego
inne, a więc może być free
d w dowolnej kolejności.
Więc, łącząc to wszystko razem, otrzymujemy to:
for (i = 0; i < m; i++) {
free(a[i]);
}
free(a);
[[30]}ostatnia wskazówka: podczas wywoływania malloc()
, rozważ 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
Oblicz, że sizeof(*a)
jest tym samym co sizeof(int *)
. Jednakże, jeśli
później zmienisz zdanie i chcesz char
s lub short
s lub long
S lub
cokolwiek w tablicy zamiast int
s, albo dostosujesz ten kod na później
użyj w czymś innym, będziesz musiał zmienić tylko jeden pozostały
odniesienie do int
w pierwszej cytowanej linijce powyżej, a wszystko else
automatycznie przypadnie Ci na miejsce. Usuwa to prawdopodobieństwo
o niezauważonych błędach w przyszłości.
Powodzenia!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 10:59:45
Cofnij dokładnie to, co przydzieliłeś:
for (i = 0; i < m; i++) {
free(a[i]);
}
free(a);
Zauważ, że musisz to zrobić w odwrotnej kolejności, z której pierwotnie przydzielono pamięć. Jeśli najpierw wykonałeś free(a)
, to a[i]
uzyskałbyś dostęp do pamięci po jej zwolnieniu, co jest niezdefiniowanym zachowaniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 10:16:51
Musisz ponownie iterować tablicę i wykonać tyle zwolnień co mallocs dla wskazanej pamięci, a następnie zwolnić tablicę wskaźników.
for (i = 0; i < m; i++) {
free (a[i]);
}
free (a);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 10:23:27
Napisz operatory alokacji w dokładnie odwróconej kolejności, zmieniając nazwy funkcji, a wszystko będzie dobrze.
//Free the arrays
for (i = m-1; i >= 0; i--) {
free(a[i]);
}
free(a);
Oczywiście, nie musiszdeallokować w tej samej odwróconej kolejności. Po prostu musisz śledzić zwolnienie tej samej pamięci dokładnie raz i nie "zapominać" wskaźników do przydzielonej pamięci(tak jak byłoby, gdybyś zwolnił a
pierwszy). Ale dealokacja w odwrotnej kolejności jest dobrą rolą kciuka, aby zająć się tym ostatnim.
Jak wskazano by litb in the comments, if allocation/deallocation had side-effects (like new
/delete
operatory w C++), czasami odwrotny porządek dealokacji byłby ważniejszy niż w tym konkretnym przykładzie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:31
Wywołał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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-14 10:40:08