C: liberar correctamente la memoria de una matriz multidimensional

7 minutos de lectura

avatar de usuario
andreas grech

Digamos 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 lo libero correctamente de la memoria ?


[Update] (Solución)

Gracias a la respuesta de Tim (y los demás), ahora puedo hacer una función de este tipo 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);
}

  • Problema de terminología: esto no es lo que C suele llamar una “matriz multidimensional”. Es solo la única forma de usar la sintaxis. a[i][j], al mismo tiempo que permite que ambas dimensiones sean desconocidas en tiempo de compilación. El otro tipo de matriz multidimensional es una matriz de matrices, en lugar de esta matriz de punteros a (los primeros elementos de) matrices.

    –Steve Jessop

    14 de noviembre de 2009 a las 18:12

OK, hay bastante confusión al explicar exactamente qué orden es necesario free() las llamadas tienen que estar presentes, así que trataré de aclarar a qué están tratando de llegar las personas y por qué.

Comenzando con lo básico, para liberar memoria que ha sido asignada usando malloc()simplemente llamas free() exactamente con el puntero que te dieron 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));

necesitas una coincidencia:

free(a[i]);

dentro de un bucle similar.

Donde esto se complica es en el orden en que debe suceder. si llamas malloc() varias veces para obtener varios fragmentos diferentes de memoria, en general, no importa en qué orden llame free() cuando hayas terminado con ellos. Sin embargo, el orden es importante aquí por una razón muy específica: estás usando una porción de malloced memoria para mantener los punteros a otros trozos de malloced memoria. Porque tú deber
no intente leer o escribir en la 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 tu liberas el a trozo en sí. Los fragmentos individuales con punteros almacenados en a[i] no dependen unos de otros, por lo que pueden ser freed en el orden que desee.

Entonces, juntando todo esto, obtenemos esto:

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

para:

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 determinar que sizeof(*a) es lo mismo que sizeof(int *). Sin embargo, si más adelante cambia de opinión y desea chars o shorts o longs o lo que sea en su matriz en lugar de ints, o adapta este código para su uso posterior en otra cosa, tendrá que cambiar solo la referencia restante a int en la primera línea citada arriba, y todo lo demás encajará automáticamente en su lugar. Esto elimina la probabilidad de errores inadvertidos en el futuro.

¡Buena suerte!

  • +1 Excelente respuesta; gracias por explicar el problema del ‘orden inverso’ y también el punto de hacer sizeof(*a)

    – Andreas Grech

    14 de noviembre de 2009 a las 11:08

  • Además, corríjame si me equivoco, pero ¿estaría en lo correcto al decir eso? sizeof(*a[i]) es equivalente a su sizeof(*(a[i]))ya que la notación de matriz [] tienen mayor precedencia que * ?

    – Andreas Grech

    14 de noviembre de 2009 a las 11:14

  • No, creo que tienes razón. es.wikipedia.org/wiki/… Sin embargo, trabajo con una regla general de que si tengo que buscarlo, probablemente otras personas que lean mi código también tendrán que buscarlo, así que para evitarles a ellos (y a mí, más adelante) problemas, el uso explícito de corchetes ayuda a aclarar cosas y ahorrar tiempo. Sin embargo, eso es solo una preferencia personal.

    – Tim

    14 de noviembre de 2009 a las 11:19

Deshacer exactamente lo que asignó:

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

Tenga en cuenta que debe hacer esto en el contrarrestar orden desde el que asignó originalmente la memoria. Si lo hiciste free(a) primero luego a[i] estaría accediendo a la memoria después de haber sido liberada, lo cual es un comportamiento indefinido.

  • Decir que tienes que liberar en orden inverso puede ser engañoso. Solo tiene que liberar la matriz de punteros después de los propios punteros.

    – Andomar

    14 de noviembre de 2009 a las 10:22

  • ¿No es otra forma de decir al revés?

    – GManNickG

    14 de noviembre de 2009 a las 10:35

  • Creo que @Andomar quiere decir que no importa en qué orden liberes el a[i]’s adentro, solo que tienes que liberarlos a todos antes de liberar a. En otras palabras, puede liberar un[0] a través de un[m-1] o un[m-1] a través de un[0] o todo el incluso un[]’s seguido por las probabilidades. Pero también estoy seguro de que @GregH no significar tuviste que hacer la a[]está en orden inverso, especialmente dado su código.

    – pax diablo

    14 de noviembre de 2009 a las 10:51

avatar de usuario
Arkaitz Jiménez

Debe iterar nuevamente 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);

avatar de usuario
P Shved

Escriba sus operadores de asignación exactamente en orden 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 lo haces tengo que desasignar en el mismo orden inverso. Solo tiene que realizar un seguimiento de la liberación de la misma memoria exactamente una vez y no “olvidar” los punteros a la memoria asignada (como hubiera sido si hubiera liberado la a primero). Pero la desasignación en el orden inverso es una buena función práctica para abordar este último.

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

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

  • ¿Cómo es esto una respuesta a la pregunta?

    – Ciego

    14 de noviembre de 2009 a las 10:49

  • Pavel Shved escribió la respuesta correcta. Acabo de escribir un comentario con algo de código.

    – sambowry

    14 de noviembre de 2009 a las 11:00

  • Debe escribir comentarios en el campo “comentario” de la pregunta. También admite bloques de código.

    – Johannes Schaub – litb

    14 de noviembre de 2009 a las 11:03

  • @litb: copie mi respuesta en un campo de comentario de la pregunta. Gracias.

    – sambowry

    14 de noviembre de 2009 a las 12:41

  • No hay razón para un voto negativo. Encuentro que la respuesta a muchas preguntas sobre SO es “no deberías hacer eso, haz esto en su lugar”. Hay espacio para respuestas a la letra de la pregunta y respuesta al significado de la pregunta.

    – jmucchiello

    14 de noviembre de 2009 a las 13:33

  • ¿Cómo es esto una respuesta a la pregunta?

    – Ciego

    14 de noviembre de 2009 a las 10:49

  • Pavel Shved escribió la respuesta correcta. Acabo de escribir un comentario con algo de código.

    – sambowry

    14 de noviembre de 2009 a las 11:00

  • Debe escribir comentarios en el campo “comentario” de la pregunta. También admite bloques de código.

    – Johannes Schaub – litb

    14 de noviembre de 2009 a las 11:03

  • @litb: copie mi respuesta en un campo de comentario de la pregunta. Gracias.

    – sambowry

    14 de noviembre de 2009 a las 12:41

  • No hay razón para un voto negativo. Encuentro que la respuesta a muchas preguntas sobre SO es “no deberías hacer eso, haz esto en su lugar”. Hay espacio para respuestas a la letra de la pregunta y respuesta al significado de la pregunta.

    – jmucchiello

    14 de noviembre de 2009 a las 13:33

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad