Mejor sería reestructurar los tipos de datos: uno para tener guardar el dato mismo y otro para guardar la cola.
Verás que no se han hecho uso de punteros dobles. Muchas veces se pueden evitar
Dato:typedef struct tDATO {
int DNI;
char * nombre;
struct tDATO *siguiente;
} *DATO;
El dato guarda la información útil más un puntero que hace la lista. En verdad se podría realizar una estructura anidada, la estructura interna con el dato y la externa que contendrá el puntero (pero eso ya más avanzado).
Cola:typedef struct tCola {
DATO primero;
DATO ultimo;
size_t n_elementos;
} *COLA;
La cola nos sirve para trabajar con la estructura cola, nada más. Guarda el primer dato, el último y el número de elementos.
Teniendo ya los dos tipos de datos estructurados vamos a crear la funcionalidad para manejarlos de forma sencilla. A por las funciones:
Para los datos .......
Primero vamos crear el datoDATO dato_crear(int dni, char *nombre) {
DATO dato
= malloc(sizeof(struct tDATO
)); dato->DNI = dni;
dato->siguiente = NULL;
return dato;
}
Esto genera un tipo dato en memoria dinámica, nada del otro mundo. Lo único que hay que tener en cuenta es que los campos que se deben alojar en memoria dinámica, como nombre, debe hacerse mediante el uso de *alloc. Esto es especialmente así sobre todo en cadenas que te pueden venir de diferentes formas y no se puede asignar directamente el valor de la variable nombre a dato->nombre por la razón de que te pueden pasar un puntero de un array local y al salir de la función esa memoria sería liberada, es decir, si te hacen esto:
DATO dato_crear(int dni, char *nombre) {
...
dato->nombre = nombre;
...
}
void generar_dato() {
...
char nombre[] = "Tirant Lo Blanc";
dato_crear(dni, nombre);
...
}
Repito, lo anterior está mal. Por otra parte se ve que 'siguiente' va a NULL, esto es así para que se puedan encontrar fallos de lógica.
Liberar dato de la memoriavoid dato_liberar(DATO dato) {
}
Antes de liberar el dato propiamente dicho hay que liberar la memoria que ha adquirido para su funcionamiento, en este caso la cadena nombre.
Como puedes haber observado este es el funcionamiento básico, no hay guardas para saber si no se han pasado datos NULL o que no hayan ocurrido errores, pero esto es a lo que vamos.
Vamos a por la cola.....
Primero se crea la colaCOLA cola_crear() {
COLA cola
= malloc(sizeof(struct tCola
)); cola->primero = NULL;
cola->ultimo = NULL;
cola->n_elementos = 0;
return cola;
}
Sencillo se genera en memoria dinámica y se ponen todos los elementos a 0 o NULL.
Agregar un dato a la colavoid cola_agregar(COLA cola, DATO dato) {
if(cola_es_vacia(cola)) {
cola->primero = dato;
} else {
cola->ultimo->siguiente = dato;
}
cola->ultimo = dato;
cola->n_elementos++;
}
Los datos a una cola se introducen por el final, nuestro dato COLA ya tiene un puntero a su último elemento así que el trabajo es sencillo. Lo único que hay que tener en cuenta es si la cola está vacía: si está vacía el elemento introducido no sólo es el último sino que también es el primero; si la cola no está vacía hay que tener presente para el buen funcionamiento de nuestra estructura de datos que el último elemento de la cola apunte al que vamos a agregar.
Extraer un dato de la colaDATO cola_extraer(COLA cola) {
DATO dato = cola->primero;
if(!cola_es_vacia(cola)) {
cola->primero = cola->primero->siguiente;
dato->siguiente = NULL;
if(cola->primero == NULL) {
cola->ultimo = NULL;
}
cola->n_elementos--;
}
return dato;
}
Como en una pila los datos se extraen desde e principio. Es importante saber dos cosas: si al principio la cola está vacía por lo que se tendrá que devolver NULL y no hacer nada más; que después de la extracción la cola se vacíe (donde cola->primero tendrá el valor NULL) en ese caso y para evitar problemas lógicos también se lleva a NULL cola->ultimo.
Desalojar la cola de memoriavoid cola_liberar(COLA cola) {
DATO x;
while(cola->primero) {
x = cola->primero;
cola->primero = cola->primero->siguiente;
dato_liberar(x);
}
}
Como con dato primero hay que liberar toda la memoria que haya alojado. Esto se hace recorriendo la lista con cola->primero hasta que sea NULL. Ver que se usa la funcion dato_liberar porque cada dato se debe desalojar usando su propia función (ya que también hace su propia limpieza). Se podrían haber extraído datos con cola_extraer, pero sería más lento.
Después, para evitar trabajar directamente con el dato cola se pueden hacer algunas
funciones auxiliares que de hecho ya se han usado en el código anterior, pero esas van más para un usuario de nuestro código:
int cola_es_vacia(COLA cola) {
return cola->n_elementos == 0;
}
size_t cola_longitud(COLA cola) {
return cola->n_elementos;
}