Bienvenidos sean a este post, hoy entraremos en accion con la memoria dinamica.
En el post anterior vimos de una forma muy resumida a que se aplica la memoria dinamica, como trabaja la memoria al momento de ejecutar un programa y por ultimo en cual segmento trabaja la memoria dinamica pero hoy nos centraremos en como manejar este tipo de memoria.
A diferencia de las automaticas este tipo de memoria no se ubica ni se libera sola sino que lo hace mediante unas llamadas especificas a funciones que se encuentran en nuestra libreria estandar, stdlib.h, para poder ubicar en la memoria dinamica tenemos dos funciones:
- calloc(), esta hace la ubicacion pero previamente libera el espacio en memoria.
- malloc(), esta tambien hace la ubicacion pero lo hace directamente
- realloc(), este sera para redeffinir el tamaño del bloque de memoria
Si miramos la libreria veremos que tiene estos prototipos para estas dos funciones:
void* malloc (size_t size);
void* calloc (size_t count, size_t size);
void* realloc (void *ptr, size_t size);
En todos los casos devolvemos un apuntador void a un bloque de memoria, de esto hablamos en este post, pero recordemos que este tipo de apuntador especial es para un tipo generico o desconocido, y este debe ser «casteado» al tipo de apuntador requerido antes de que puedas usar ese apuntador, la otra diferencia esta en que malloc solo tiene un parametro de tamaño mientras que calloc posee dos, uno para el tamaño y otro para contar, en caso de que la funcion no encuentre memoria en el espacio de heap el apuntador que devolvera sera NULL la ultima funcion observen que recibe un apuntador de este tipo porque deberemos pasar alguno generado con los anteriores y el segundo valor sera el nuevo tamaño que le asignaremos, este ultimo tambien debe ser «casteado» al tipo necesario para ser usado, una buena practica es chequear si estas rutinas fueron bien realizadas, si tomamos el codigo del juego de cartas que vimos en varios posts, por ejemplo para hacer un apuntador con malloc con una sola carta podemos hacerlo de la siguiente forma:
Carta* aCarta1 = (Carta*) malloc(sizeof(Carta));
Y para el caso de calloc seria de la siguiente forma:
Carta* aCarta1 = (Carta*) calloc(1, sizeof(Carta));
Observen como a diferencia del anterior debemos especificar la cantidad de cartas, en este caso una, pero que sucede si necesitamos pasar las cartas a un jugador, veamos el caso para malloc:
Carta* aJugador = (Carta*) malloc(3 * sizeof(Carta));
Como pueden observar al tamaño lo debemos multiplicar por la cantidad de elementos que pasaremos, veamos como seia el mismo caso pero para calloc:
Carta* aJugador = (Carta*) calloc(3, sizeof(Carta));
Tal como hicimos en el caso anterior simplemente le pasamos la cantidad de elementos que agregaremos en memoria, en todos los casos si queremos comprobar que no haya fallado podemos usar un condicional como el siguiente:
if (aCarta1 == NULL) { instrucciones para manejar el error }
En el segundo ejemplo podemos ver como se asemeja a un array, porque al igual que en un array estamos estableciendo cinco elementos a almacenar en ese bloque de memoria los cuales seran correlativos igual que en un array, si recordamos cuando hablamos de la intercambiabilidad entre array y apuntadores en este post, trabajando de esta forma podemos acceder a informacion que puede estar almacenada tanto en el heap como en el stack.
Si son tan parecidas y en cierto punto intercambiables porque no usar una sola? en realidad siempre va a quedar a nuestro criterio dado que ambos ubicaran un espacio en memoria para nuestra informacion pero recuerden que calloc limpiara primero el espacio, es decir que lo llenara de ceros antes de la informacion, en cambio malloc lo hace directamente pero como podran darse cuenta uno sera mas seguro (calloc) que el otro pero uno (malloc) sera mas rapido que el otro, y eso se debera ajustar a la necesidad de cada uno.
Por ultimo mencionamos que tenemos una funcion llamada realloc la cual nos permite redefinir el tamaño de este espacio, mencionamos que entre los parametros que recibe son el apuntador a modificar y el nuevo tamaño, en caso de ser de mas grande se copiara la informacion previa y el nuevo espacio estara sin iniciar, en cambio si es mas chico la informacion sera truncada o cortada hasta el nuevo tamaño, por ultimo si el apuntador es NULL trabajara de la misma forma que malloc.
Nota: si tienen curiosidad por el tipo size_t, es un tipo definido tambien en stdlib.h de la siguiente forma: type unsigned int size_t;
Para poder acceder a esta inforrmacion es muy simple, vamos a suponer que seguimos con nuestro codigo del juego de cartas, para iniciar una carta tenemos un metodo y para ello podemos usar el apuntador de la carta de la siguiente manera:
IniciarCarta(aCarta1, basto, dos);
Como este apuntador es uno a estructura de cartas independiente podemos utilizarlo como si fuera uno de las variables automaticas del codigo propio, pero consideremos el siguiente ejemplo:
aJugador[0].palo = basto;
aJugador[0].numero = dos;
for(int i=0; i < cCartasEnMano; i++)
{
MostrarCarta(&(aJugador[i]));
}
En este caso aJugador apunta a un bloque de secuencia que contiene tres estructuras de cartas, lo equivalente a la cantidad de cartas que tiene en mano un jugador, tal como venimos diciendo mediante la notacion de array pudimos pasar un dato a nuestro apuntador que posee dicha estructura en esa posicion, observen que en el caso del bucle volvemos a usar lo mismo, si bien este es un ejemplo poco practico y no debemos hacerlo de esta forma, es la forma mas sencilla de mostrar como trabaja sin mencionar otros temas que veremos mas adelante.
Hasta aqui ya cubrimos como asignar un espacio de memoria para un tipo determinado en el heap, asi como tambien trabajar con el pero nuestro siguiente tema sera como liberar este espacio una vez que hayamos terminado de trabajar con el, aqui entra en accion la funcion free, la cual se encargara de devolver la memoria ubicada al pool de memoria de heap disponible, esta no tiene que ser llamada en la misma funcion donde se ubico la memoria sino cuando lo necesitemos, al igual que en los casos anteriores veamos como es su prototipo:
void free(void* ptr);
En este caso no necesiatmos «castear» nada sino simplemente pasar el apuntador que creamos mediante malloc o calloc, si pasamos NULL como apuntador este no hara nada, por ejemplo para liberar el apuntador anterior simplemente hariamos esto:
free(aCarta1);
free(aJugador);
Con esto cubrimos lo basico para poder trabajar con la memoria dinamica, en los proximos posts ahondaremos un poco mas sobre estos temas.
En resumen, hoy hemos visto como manejar la memoria dinamica, hemos cubierto desde como se asigma, como se accede, y por ultimo como se elimina o libera este espacio de la memoria heap, espero les haya sido de utlidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
