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 el **a, ¿cómo puedo liberarlo correctamente de la memoria ?


[Actualización] (Solución)

Gracias a la respuesta de Tim (y los demás) , ahora puedo hacer tal 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);
}
Author: Community, 2009-11-14

5 answers

OK, hay un trato justo de confusión explicando exactamente qué orden el free() llamadas necesarias tienen que estar en, así que voy a tratar de aclarar lo que la gente está tratando de llegar y por qué.

Comenzando con lo básico, para liberar memoria que ha sido asignada usando malloc(), simplemente llama a free() con exactamente el puntero que te fue dado por malloc(). Así que 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));

Usted necesita un coincidencia:

free(a[i]);

Dentro de un bucle similar.

Donde esto se complica es el orden en el que esto tiene que suceder. Si usted llama malloc() varias veces para obtener varios trozos diferentes de memoria, en general no importa qué orden llame free() cuando has terminado con ellos. Sin embargo, el orden es importante aquí para un muy razón específica: está utilizando un trozo de mallocmemoria ed para mantener los punteros a otros trozos de memoria ed malloc. Porque debe no intente leer o escribir memoria una vez que la haya devuelto con free(), esto significa que vas a tener que liberar los trozos con sus punteros almacenados en a[i] antes de se libera el a trozo en sí. Los trozos individuales con punteros almacenados en a[i] no dependen de cada uno otro, y así puede ser free d en el orden que desee.

Así que, juntando todo esto, obtenemos esto: {[31]]}

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

Un último consejo: al llamar 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 compilador sabe que a es un int **, por lo que puede determine que sizeof(*a) es lo mismo que sizeof(int *). Sin embargo, si más tarde cambias de opinión y quieres char s o short s o long s o lo que sea en su matriz en lugar de ints, o adaptar este código para más tarde use en otra cosa, tendrá que cambiar solo el que queda referencia a int en la primera línea citada arriba, y todo else automáticamente caerá en su lugar para usted. Esto elimina la probabilidad de errores inadvertidos en el futuro.

¡Buena suerte!

 60
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 10:59:45

Deshacer exactamente lo que ha asignado:

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

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

 8
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 10:16:51

Necesita iterar nuevamente la matriz y hacer tantos frees como mallocs para la memoria apuntada, y luego liberar la matriz de punteros.

for (i = 0; i < m; i++) { 
      free (a[i]);
}
free (a);
 4
Author: Arkaitz Jimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 10:23:27

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

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

  free(a);

Por supuesto, no tienes que desasignar en el mismo orden inverso. Solo tienes que hacer un seguimiento de cómo liberar la misma memoria exactamente una vez y no "olvidar" los punteros a la memoria asignada (como habría sido si hubieras liberado el a primero). Pero la desasignación en el orden inverso es un buen papel del pulgar para abordar este último.

Como apuntado by litb in the comments, if allocation / deallocation had side-effects (like new/delete operadores en C++), a veces el orden hacia atrás de la desasignación sería más importante que en este ejemplo en particular.

 4
Author: P Shved,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:31

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

#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;
}
 1
Author: sambowry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-14 10:40:08