Ver Mensaje Individual
  #2 (permalink)  
Antiguo 16/11/2013, 04:22
vosk
 
Fecha de Ingreso: agosto-2012
Mensajes: 601
Antigüedad: 11 años, 8 meses
Puntos: 83
Respuesta: vector de punteros a estructuras

"...pero no se para que puede servir..."

Suele usarse para listas de datos, donde cada elemento puede relacionarse con cualquier otro; lo mas facil es que un elemento se relacione con el siguiente de forma que obtienes una lista enlazada simple.

Supongamos que tienes una novia en cada pueblo, y quieres crear una lista para no confundirte :)

Primero declaras una struct que identifica el elemento y la relacion que tiene con cualquier otro (u otros):

Código C:
Ver original
  1. struct NOVIA {
  2.     char nombre[20];
  3.     char ciudad[50];
  4.     char fecha_cita[20];
  5.     struct NOVIA *siguiente;
  6. };

Ahora declaras una lista; una lista es un simple puntero inicialmente nulo:

Código C:
Ver original
  1. struct NOVIA *amorios;
  2. amorios = 0;

Luego pasas a implementar la lista; traducido significa que reservas memoria para una struct (es decir que obtienes un puntero a una direccion de memoria) y la envias a la posicion de la lista que quieras. Habitualmente si la lista es nula se asigna el primer elemento como base de la lista, y los siguientes se asignan como elemento 'siguiente' del ultimo elemento de la lista:

Código C:
Ver original
  1. struct NOVIA *novia;
  2.  
  3. novia = malloc(sizeof(struct NOVIA));
  4. sprintf(novia->nombre, "samanta fox");
  5. novia->siguiente = 0;
  6. amorios = novia;
  7.  
  8. novia = malloc(sizeof(struct NOVIA));
  9. sprintf(novia->nombre, "quin sea");
  10. novia->siguiente = 0;
  11. amorios->siguiente = novia;

Ok, ya se que el ejemplo es un poco tonto, pero ya te has quedado con la idea de que va eso de las listas.

Otro punto importante de las listas es liberar la memoria usada, es decir que para cada malloc necesitas un free.

Todo esto suele serializarse, es decir que tienes una funcion que reserva memoria para un elemento, busca en la lista la ultima posicion y añade el nuevo elemento, luego tienes otra que busca algun dato, luego otras que hacen lo que quieras, y una ultima funcion que libera la memoria usada.

Esta es la declaracion basica de una lista simple (ahora sin novias):

Código C:
Ver original
  1. //soporte de datos
  2. struct DATO {
  3.     int numero;
  4.     struct DATO *sig;
  5. };
  6.  
  7.  
  8. void nuevo(struct DATO **lista, int numero) {
  9.     struct DATO *dato, *ptr;
  10.  
  11.     //reservo memoria para el nuevo dato
  12.     dato = malloc(sizeof(struct DATO));
  13.     dato->numero = numero;
  14.     dato->sig = 0;
  15.  
  16.     //si la lista no existe, se asigna el nuevo dato como base
  17.     if(!(ptr = *lista)) {
  18.         *lista = dato;
  19.     }
  20.     else {
  21.         //si la lista existe, se busca el ultimo elemento y se asigna el nuevo como siguiente
  22.         while(ptr->sig) {
  23.             ptr = ptr->sig;
  24.         }
  25.         ptr->sig = dato;
  26.     }
  27. }
  28.  
  29.  
  30. void muestra(struct DATO *lista) {
  31.     struct DATO *ptr;
  32.  
  33.     //simplemente recorre la lista
  34.     ptr = lista;
  35.     while(ptr) {
  36.         printf("%d\n", ptr->numero);
  37.         ptr = ptr->sig;
  38.     }
  39. }
  40.  
  41.  
  42. void vacia(struct DATO **lista) {
  43.     struct DATO *ptr, *sig;
  44.  
  45.     ptr = *lista;
  46.     while(ptr) {
  47.         //guarda la posicion del siguiente para recuperarla despues de eliminar el actual
  48.         sig = ptr->sig;
  49.         free(ptr);
  50.         //ahora recuperas la posicion guardada antes
  51.         //en este momento ptr->siguiente ya no está dentro del bloque de memoria accesible
  52.         //y por eso necesitas el recordatorio para seguir
  53.         ptr = sig;
  54.     }
  55.     *lista = 0;
  56. }

Ahora las llamadas:

Código C:
Ver original
  1. struct DATO *lista;
  2. int q;
  3.  
  4. //confirmas la lista como nula
  5. lista = 0;
  6.  
  7. //la llenas
  8. for(q = 0; q < 5; q++) {
  9.     nuevo(&lista, q);
  10. }
  11.  
  12. //mostrar
  13. muestra(lista);
  14.  
  15. //y la vacias
  16. vacia(&lista);

Dos cosas a tener en cuenta: cuando trabajas com memoria dinamica el s.o. suele asignarte bloques de memoria reutilizados, es decir que pueden contener cualquier cosa, es decir que si declaras un entero no valdrá 0 por defecto sino que valdrá el equivalente a entero de la posicion de memoria que se le asigna, por eso todo lo que reservas y quieres que sea nulo tienes que asignarle manualmente nulo; es el caso de cuando inicias la lista a nulo (0), o de cuando creas el nuevo elemento y necesitas que el siguiente elemento sea nulo.

La otra cosa a tener en cuenta: siempre que trabajas con listas tienes que evitar trabajar directamente sobre la base a menos que tengas una forma de restaurar el puntero a la posicion de memoria inicial (cuando pierdes el puntero de la base se produce una peridia de memoria o memory leak, es decir que durante toda la aplicacion ese bloque de memoria será inaccesible y no reutilizable).

Este ejemplo es lo mas basico, las listas se pueden complicar mas. Puedes ordenarlas (intercambiando los punteros), puedes quitar elementos que hay enmedio, puedes reasignar valores, etc...

Esto de las listas es muy interesante y bonito de programar, ademas puedes implementarlas en cualquier aplicacion.

Saludos
vosk