C: liberar correctamente la memoria de una matriz multidimensional

Supongamos que tiene el siguiente código ANSI C que inicializa una matriz multidimensional:

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

Después de usar **a , ¿cómo puedo liberarlo de la memoria correctamente?


[Actualización] (Solución)

Gracias a la respuesta de Tim (y los demás), ahora puedo hacer una función para liberar memoria de mi matriz multidimensional:

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

De acuerdo, hay una buena cantidad de confusión explicando exactamente en qué orden deben estar las llamadas free() , así que intentaré aclarar a qué están tratando de llegar las personas y por qué.

Comenzando con los conceptos básicos, para liberar memoria que ha sido asignada usando malloc() , simplemente llame a free() exactamente con el puntero que le dio malloc() . Entonces para este código:

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

necesitas una coincidencia:

 free(a); 

y para esta línea:

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

necesitas una coincidencia:

 free(a[i]); 

dentro de un bucle similar.

Donde esto se complica es el orden en que esto debe suceder. Si llama a malloc() varias veces para obtener diferentes fragmentos de memoria, en general no importa el orden al que llame free() cuando haya terminado con ellos. Sin embargo, el orden es importante aquí por una razón muy específica: está utilizando un trozo de memoria malloc para mantener los punteros en otros trozos de memoria malloc ed. Como no debe intentar leer o escribir memoria una vez que la haya devuelto con free() , esto significa que tendrá que liberar los fragmentos con sus punteros almacenados en a[i] antes de liberar el fragmento en sí. Los fragmentos individuales con punteros almacenados en a[i] no son dependientes entre sí, por lo que se pueden free en cualquier orden que desee.

Entonces, juntando todo esto, obtenemos esto:

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

Un último consejo: cuando llame a malloc() , considere cambiar estos:

 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]))); 

¿Qué está haciendo esto? El comstackdor sabe que a es un int ** , por lo que puede determinar que sizeof(*a) es lo mismo que sizeof(int *) . Sin embargo, si más adelante cambias de parecer y quieres char o short o long o lo que sea en tu matriz en lugar de int , o adaptas este código para usarlo más tarde en otra cosa, tendrás que cambiar solo el que queda referencia a int en la primera línea citada arriba, y todo lo demás se colocará automáticamente en su lugar. Esto elimina la posibilidad de errores inadvertidos en el futuro.

¡Buena suerte!

Deshaga exactamente lo que asignó:

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

Tenga en cuenta que debe hacer esto en el orden inverso al que asignó originalmente la memoria. Si lo hizo free(a) primero, entonces a[i] accedería a la memoria después de haber sido liberado, lo cual es un comportamiento indefinido.

Necesita iterar de nuevo la matriz y hacer tantas liberaciones como mallocs para la memoria apuntada, y luego liberar la matriz de punteros.

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

Escriba sus operadores de asignación exactamente en orden inverso, cambiando los nombres de las funciones, y todo estará bien.

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

Por supuesto, no tiene que desasignar en el mismo orden invertido. Solo tiene que hacer un seguimiento para liberar la misma memoria exactamente una vez y no “olvidar” los punteros a la memoria asignada (como lo hubiera sido si hubiera liberado la a primero). Pero la desasignación en el orden inverso es un buen rol para abordar este último.

Como se señaló en los comentarios, si la asignación / desasignación tenía efectos colaterales (como los operadores new / delete en C ++), a veces el orden inverso de la desasignación sería más importante que en este ejemplo particular.

Llamaría malloc () y free () solo una vez:

 #include  #include  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