Professional Documents
Culture Documents
800x600 mínimo
En esta lección:
¿Qué es la
asignación dinámica?
----- ¿Qué es la asignación dinámica?
Un array de
punteros
----- Hasta este punto del tutorial de C las variables que se han utilizado en los programas son de tipo
Una lista enlazada
----- estáticas. (Algunas de ellas han sido automáticas y fueron asignadas dinámicamente para Usted
por el sistema pero esta operación pasó inadvertida para Usted). En este capítulo estudiaremos
Otras secciones: algunas variables asignadas dinámicamente, éstas son variables que no existen cuando se carga
el programa pero se crean dinámicamente cuando son necesarias al correr el programa. Es
Conceptos básicos
----- posible, utilizando éstas técnicas crear tantas variables como sea necesario, utilizarlas y
Programando en C removerlas de su espacio en memoria para que sea utilizado por otras variables, como es
-----
Programando en C+ costumbre, un ejemplo habla bién del concepto:
+
-----
Programando
Windows 9x. #include <stdio.h>
----- #include <string.h>
Teoría electrónica #include <stdlib.h>
-----
Circuitos electrónicos
----- struct animal
Actividades {
adicionales char nombre[25];
-----
Hipervínculos
char raza[25];
----- int edad;
}
*mascota1, *mascota2, *mascota3;
Contácteme:
return 0;
}
Empezamos definiendo una estructura llamada animal con algunos campos en relación a
unos perros, no definimos ninguna variable de éste tipo sólo tres punteros, si Usted busca
en el resto del código del programa no encontrará ninguna variable definida por lo que no
tenemos en donde almacenar datos, todo lo que tenemos para trabajar son tres punteros,
cada uno de los cuales es capaz de señalar a variables de la estructura definida llamada
animal. Para hacer cualquier cosa con el programa necesitamos algunas variables por lo
que las crearemos dinámicamente
Espero que la breve explicación de la pila y la asignación dinámica sea suficiente para
entender lo que estamos haciendo con la función malloc ( ). Simplemente le solicita al
sistema un bloque de memoria del tamaño especificado regresando un puntero que señala
al primer elemento del bloque, el único argumento en el paréntesis es el tamaño deseado
del bloque que en nuestro caso, deseamos un bloque que almacene una de las estructuras
que definimos al principio del programa. El operador sizeof es nuevo, al menos para
nosotros en éste curso, regresa el tamaño en bytes del argumento entre paréntesis, regresa
por lo tanto, el tamaño de la estructura llamada animal y ése número es utilizado como
parámetro en la llamada a malloc ( ), al completar ésta llamada tenemos un bloque
asignado en la memoria con el puntero llamado mascota1 señalando el principio de éste
bloque.
Volver al principio
Un array de punteros
Parece que la explicación de la asignación dinámica en el anterior ejemplo fué muy extensa, la
buena noticia es que se ha dicho prácticamente todo al respecto, claro está que aún falta por
aprender algunas técnicas en el uso de la asignación dinámica y esto es lo que haremos en los
siguientes ejemplos, empezando con el siguiente código:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct animal
{
char nombre[25];
char raza[25];
int edad;
}
*mascota[12], *puntero; /* Esto define 13 punteros, no variables */
int main()
{
int indice;
Este programa es muy parecido al ejemplo anterior ya que se utiliza la misma estructura
pero en el presente caso definimos un array de punteros para ilustrar los mecanismos para
construir una base de datos grande utilizando un array de punteros en lugar de un puntero
sencillo a cada elemento, para mantener el ejemplo sencillo definimos 12 elementos en el
array y otro puntero adicional llamado puntero. El enunciado *mascota[12] es nuevo así
que es conveniente hacer algunos comentarios. Lo que hemos definido es un array de 12
punteros siendo el primero mascota[0] y el último mascota[11], como un array es en sí
un puntero, el nombre mascota es un puntero constante a otro puntero, ésto es válido en
C, no hay limites en cuanto al número de niveles señalados, así, por ejemplo, la expresión
int ****pt es legal ya que se trata de un puntero a un puntero a un puntero a un puntero
a una variable de tipo entero, se recomienda tal uso de los punteros hasta haber ganado
suficiente experiencia en su manejo.
Ahora que tenemos 12 punteros los cuales pueden usarse como cualquier otro, es sencillo
escribir un bucle para asignar dinámicamente un bloque de datos para cada puntero y
rellenar los respectivos campos con los datos deseados, en éste caso rellenamos los
espacios con datos de propósito ilustrativo pero bién podría tratarse de una base de datos,
de información proveniente de algún equipo de prueba de laboratorio ó cualquier otra
fuente de datos. Notará que en el ejemplo checamos cuidadosamente el valor retornado por
la función malloc ( ) para verificar que no contenga un valor de cero, si retorna un valor
NULL, desplegamos un mensaje indicandolo y terminando el programa, recuerde que en un
programa real no basta con terminar el programa, es aconsejable generar un reporte de
errores y darle al usuario la oportunidad de corregirlos y continuar el proceso iniciado antes
de cerrar el programa. Volviendo al análisis del código, en las líneas 31 a la 33 escogimos
algunos campos al azar para ilustrar el uso de enunciados sencillos y que los datos son
desplegados en el monitor. El puntero puntero se utiliza en el bucle para desplegar datos
sólo para ilustración, fácilmente se hubiera podido utilizar mascota[indice] en su lugar.
Finalmente los 12 bloques son liberados de la memoria antes de terminar el programa, el
resultado de la ejecución es el siguiente, tenga en cuenta que mi compilador no maneja la
letra eñe:
Volver al principio
#define REGISTROS 6
struct animal
{
char nombre[25]; /* El nombre del animal */
char raza[25]; /* El tipo de animal */
int edad; /* La edad del animal */
struct animal *siguiente; /* señala a otra estructura de este tipo */
}
*puntero, *inicio, *previo; /* Se definen tres punteros */
int main()
{
/* El primer registro siempre es un caso especial */
inicio = (struct animal *)malloc(sizeof(struct animal));
if (inicio == NULL)
{
printf("Falla en la asignacion de memoria\n");
exit (EXIT_FAILURE);
}
strcpy(inicio->nombre, "General");
strcpy(inicio->raza, "Pastor para tacos");
inicio->edad = 4;
inicio->siguiente = NULL;
previo = inicio;
strcpy(puntero->nombre, "Pancho");
strcpy(puntero->raza, "Labrador");
puntero->edad = 3;
/* señala al ultimo "siguiente" de este registro */
previo->siguiente = puntero;
puntero->siguiente = NULL; /* señala este "siguiente" a NULL */
previo = puntero; /* Este es ahora el registro previo */
}
return EXIT_SUCCESS;
}
Este programa inicia de una manera similar a los ejemplos anteriores con la adición de una
declaración constante para uso posterior, la estructura es casi la misma excepto por la
adición de otro campo dentro de la estructura en la línea 12, se trata de un puntero a otra
estructura del mismo tipo y será utilizada para señalar a la siguiente estructura en orden,
de acuerdo a la analogía de arriba, éste puntero señalará a la siguiente nota, que a su vez
contendrá un puntero a la siguiente nota. Definimos tres punteros y una variable para
utilizarla como contador y con ésto estamos listos para empezar a utilizar la estructura
definida, una vez más con datos sin sentido, sólo para propósitos de ilustración.
El primer campo
Utilizando la función malloc ( ) solicitamos un bloque de almacenamiento en memoria (en
el heap) y lo rellenamos con datos, checando la correcta asignación. Al campo adicional, en
éste ejemplo llamado siguiente, se le asigna el valor de NULL, el cual es utilizado para
indicar que éste es el final de la lista, dejaremos el puntero llamado inicio señalando a ésta
estructura de tal manera que siempre señalará a la primera estructura de la lista.
Asignamos además a previo el valor de inicio por razones que pronto veremos. Tenga en
cuenta que los extremos de la lista enlazada serán siempre manejados de forma diferente a
los elementos centrales de la lista, en éste punto tenemos un elemento en nuestra lista
relleno de datos significativos.
Debe quedar claro que no es posible simplemente brincar a mitad de la lista y cambiar
algunos valores, la única manera de acceder, por ejemplo, a la tercera estructura es
iniciando por el principio y recorrer la lista una estructura a la vez, aunque parece un precio
alto por la conveniencia de colocar algo de datos fuera del area del programa, es de hecho
una buena forma de almacenar cierto tipo de datos.
Para desplegar los datos se utiliza un método similar al utilizado para generar los datos, los
punteros son inicializados y entonces utilizados para avanzar de registro en registro,
leyendo y desplegando cada registro a la vez. El despliegue termina cuando se encuentra un
NULL, así que el programa no necesita saber siquiera cuántos registros hay en la lista.
Finalmente borramos la lista completa para liberar espacio en memoria. Se debe tener
cuidado de no borrar el último registro antes de checar el valor NULL, ya que sería
imposible terminar el programa.
Volver al principio