Cuestión 1 — Traza de memoria paso a paso
Conviene distinguir dos zonas de memoria:
- Pila (stack): la variable local
Cola c de main, inicializada a { cabeza = NULL, cola = NULL }. Contiene dos punteros.
- Montón (heap): los nodos creados con
malloc, que llamaremos N1, N2, N3.
Estado inicial (antes de encolar nada):
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 120" font-family="ui-sans-serif, system-ui, Arial, sans-serif">
<rect x="20" y="20" width="260" height="80" rx="6" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="32" y="40" font-size="12" font-weight="bold" fill="#2563eb">Pila (stack)</text>
<rect x="40" y="52" width="100" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1"/>
<text x="90" y="70" font-size="11" text-anchor="middle" fill="#0f172a">c.cabeza</text>
<text x="90" y="82" font-size="11" text-anchor="middle" fill="#0f172a">NULL</text>
<rect x="160" y="52" width="100" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1"/>
<text x="210" y="70" font-size="11" text-anchor="middle" fill="#0f172a">c.cola</text>
<text x="210" y="82" font-size="11" text-anchor="middle" fill="#0f172a">NULL</text>
<rect x="320" y="20" width="260" height="80" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="332" y="40" font-size="12" font-weight="bold" fill="#059669">Montón (heap)</text>
<text x="450" y="72" font-size="12" text-anchor="middle" font-style="italic" fill="#0f172a">(vacío)</text>
</svg>
Tras encolar(&c, 101, 3) → se reserva N1. Como c->cabeza == NULL, se ejecuta la rama then: c->cabeza = N1. Después, incondicionalmente, c->cola = N1.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 170" font-family="ui-sans-serif, system-ui, Arial, sans-serif">
<text x="20" y="30" font-size="12" font-weight="bold" fill="#2563eb">Pila</text>
<rect x="20" y="40" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="56" font-size="11" text-anchor="middle" fill="#0f172a">c.cabeza</text>
<text x="75" y="70" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N1</text>
<rect x="20" y="95" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="111" font-size="11" text-anchor="middle" fill="#0f172a">c.cola</text>
<text x="75" y="125" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N1</text>
<text x="360" y="30" font-size="12" font-weight="bold" fill="#059669">Heap</text>
<rect x="360" y="55" width="180" height="64" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="372" y="73" font-size="12" font-weight="bold" fill="#0f172a">N1</text>
<text x="372" y="91" font-size="11" fill="#0f172a">id=101 paginas=3</text>
<text x="372" y="109" font-size="11" fill="#0f172a">sig = NULL</text>
<line x1="130" y1="60" x2="352" y2="78" stroke="#2563eb" stroke-width="1.5"/>
<polygon points="352,78 342,74 344,82" fill="#2563eb"/>
<line x1="130" y1="115" x2="352" y2="100" stroke="#2563eb" stroke-width="1.5"/>
<polygon points="352,100 342,98 343,106" fill="#2563eb"/>
</svg>
Tras encolar(&c, 102, 12) → se reserva N2. Ahora c->cabeza != NULL, así que se ejecuta la rama else: c->cola->sig = N2 (es decir, N1.sig = N2). Luego c->cola = N2.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 180" font-family="ui-sans-serif, system-ui, Arial, sans-serif">
<defs>
<marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#059669"/></marker>
<marker id="arrow-blue" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#2563eb"/>
</marker>
</defs>
<text x="20" y="30" font-size="12" font-weight="bold" fill="#2563eb">Pila</text>
<rect x="20" y="40" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="56" font-size="11" text-anchor="middle" fill="#0f172a">c.cabeza</text>
<text x="75" y="70" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N1</text>
<rect x="20" y="100" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="116" font-size="11" text-anchor="middle" fill="#0f172a">c.cola</text>
<text x="75" y="130" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N2</text>
<text x="320" y="30" font-size="12" font-weight="bold" fill="#059669">Heap</text>
<rect x="320" y="45" width="170" height="64" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="332" y="63" font-size="12" font-weight="bold" fill="#0f172a">N1</text>
<text x="332" y="81" font-size="11" fill="#0f172a">id=101 paginas=3</text>
<text x="332" y="99" font-size="11" fill="#0f172a">sig = N2</text>
<rect x="520" y="45" width="170" height="64" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="532" y="63" font-size="12" font-weight="bold" fill="#0f172a">N2</text>
<text x="532" y="81" font-size="11" fill="#0f172a">id=102 paginas=12</text>
<text x="532" y="99" font-size="11" fill="#0f172a">sig = NULL</text>
<line x1="130" y1="58" x2="318" y2="68" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<path d="M 130 118 H 300 V 135 H 518 V 86" fill="none" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<line x1="490" y1="90" x2="518" y2="80" stroke="#059669" stroke-width="1.5" marker-end="url(#arrow-green)"/>
</svg>
Tras encolar(&c, 103, 1) → se reserva N3. Rama else: c->cola->sig = N3 (N2.sig = N3). Luego c->cola = N3.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 210" font-family="ui-sans-serif, system-ui, Arial, sans-serif">
<text x="20" y="30" font-size="12" font-weight="bold" fill="#2563eb">Pila</text>
<rect x="20" y="40" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="56" font-size="11" text-anchor="middle" fill="#0f172a">c.cabeza</text>
<text x="75" y="70" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N1</text>
<rect x="20" y="120" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="136" font-size="11" text-anchor="middle" fill="#0f172a">c.cola</text>
<text x="75" y="150" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N3</text>
<text x="220" y="30" font-size="12" font-weight="bold" fill="#059669">Heap</text>
<rect x="180" y="45" width="160" height="60" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="192" y="63" font-size="12" font-weight="bold" fill="#0f172a">N1</text>
<text x="192" y="80" font-size="10" fill="#0f172a">id=101 pag=3</text>
<text x="192" y="96" font-size="10" fill="#0f172a">sig = N2</text>
<rect x="380" y="45" width="160" height="60" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="392" y="63" font-size="12" font-weight="bold" fill="#0f172a">N2</text>
<text x="392" y="80" font-size="10" fill="#0f172a">id=102 pag=12</text>
<text x="392" y="96" font-size="10" fill="#0f172a">sig = N3</text>
<rect x="580" y="45" width="160" height="60" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="592" y="63" font-size="12" font-weight="bold" fill="#0f172a">N3</text>
<text x="592" y="80" font-size="10" fill="#0f172a">id=103 pag=1</text>
<text x="592" y="96" font-size="10" fill="#0f172a">sig = NULL</text>
<line x1="130" y1="58" x2="172" y2="68" stroke="#2563eb" stroke-width="1.5"/>
<polygon points="172,68 162,65 163,73" fill="#2563eb"/>
<line x1="340" y1="80" x2="372" y2="80" stroke="#059669" stroke-width="1.5"/>
<polygon points="372,80 362,76 362,84" fill="#059669"/>
<line x1="540" y1="80" x2="572" y2="80" stroke="#059669" stroke-width="1.5"/>
<polygon points="572,80 562,76 562,84" fill="#059669"/>
<line x1="130" y1="138" x2="640" y2="108" stroke="#2563eb" stroke-width="1.5"/>
<polygon points="640,108 630,107 632,115" fill="#2563eb"/>
</svg>
Primer desencolar(&c) → n = c->cabeza (= N1); id = 101; c->cabeza = n->sig (= N2); free(N1); devuelve 101. Imprime Atiende 101.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 200" font-family="ui-sans-serif, system-ui, Arial, sans-serif">
<defs>
<marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#059669"/></marker>
<marker id="arrow-blue" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#2563eb"/>
</marker>
</defs>
<text x="20" y="30" font-size="12" font-weight="bold" fill="#2563eb">Pila</text>
<rect x="20" y="40" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="56" font-size="11" text-anchor="middle" fill="#0f172a">c.cabeza</text>
<text x="75" y="70" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N2</text>
<rect x="20" y="115" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="131" font-size="11" text-anchor="middle" fill="#0f172a">c.cola</text>
<text x="75" y="145" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N3</text>
<text x="270" y="30" font-size="12" font-weight="bold" fill="#059669">Heap</text>
<rect x="220" y="40" width="160" height="60" rx="6" fill="#f8fafc" stroke="#334155" stroke-width="1" stroke-dasharray="4 3"/>
<text x="300" y="74" font-size="11" text-anchor="middle" font-style="italic" fill="#334155">N1 liberado</text>
<rect x="220" y="120" width="160" height="60" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="232" y="138" font-size="12" font-weight="bold" fill="#0f172a">N2</text>
<text x="232" y="155" font-size="10" fill="#0f172a">id=102 pag=12</text>
<text x="232" y="171" font-size="10" fill="#0f172a">sig = N3</text>
<rect x="500" y="120" width="160" height="60" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="512" y="138" font-size="12" font-weight="bold" fill="#0f172a">N3</text>
<text x="512" y="155" font-size="10" fill="#0f172a">id=103 pag=1</text>
<text x="512" y="171" font-size="10" fill="#0f172a">sig = NULL</text>
<line x1="130" y1="62" x2="218" y2="135" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<line x1="380" y1="150" x2="498" y2="150" stroke="#059669" stroke-width="1.5" marker-end="url(#arrow-green)"/>
<path d="M 130 135 H 180 V 192 H 498 V 160" fill="none" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
</svg>
Segundo desencolar(&c) → n = c->cabeza (= N2); id = 102; c->cabeza = n->sig (= N3); free(N2); devuelve 102. Imprime Atiende 102.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 660 175" font-family="ui-sans-serif, system-ui, Arial, sans-serif">
<defs>
<marker id="arrow-blue" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="#2563eb"/>
</marker>
</defs>
<text x="20" y="30" font-size="12" font-weight="bold" fill="#2563eb">Pila</text>
<rect x="20" y="40" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="56" font-size="11" text-anchor="middle" fill="#0f172a">c.cabeza</text>
<text x="75" y="70" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N3</text>
<rect x="20" y="100" width="110" height="40" rx="4" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/>
<text x="75" y="116" font-size="11" text-anchor="middle" fill="#0f172a">c.cola</text>
<text x="75" y="130" font-size="10" text-anchor="middle" font-style="italic" fill="#2563eb">→ N3</text>
<text x="300" y="30" font-size="12" font-weight="bold" fill="#059669">Heap</text>
<rect x="220" y="40" width="160" height="40" rx="6" fill="#f8fafc" stroke="#334155" stroke-width="1" stroke-dasharray="4 3"/>
<text x="300" y="64" font-size="11" text-anchor="middle" font-style="italic" fill="#334155">N1 y N2 liberados</text>
<rect x="420" y="55" width="180" height="64" rx="6" fill="#ecfdf5" stroke="#334155" stroke-width="1.5"/>
<text x="432" y="73" font-size="12" font-weight="bold" fill="#0f172a">N3</text>
<text x="432" y="91" font-size="11" fill="#0f172a">id=103 paginas=1</text>
<text x="432" y="109" font-size="11" fill="#0f172a">sig = NULL</text>
<path d="M 130 60 H 200 V 28 H 418 V 70" fill="none" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<line x1="130" y1="120" x2="418" y2="98" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<text x="300" y="150" font-size="11" text-anchor="middle" font-style="italic" fill="#059669">cola sigue apuntando a N3 (correcto: sigue vivo)</text>
</svg>
Salida exacta por consola, en este orden:
Atiende 101
Atiende 102
Justificación FIFO frente a LIFO. Los nodos entran siempre por la cola (c->cola->sig = n; c->cola = n) y salen siempre por la cabeza (c->cabeza = n->sig). Como se inserta por un extremo y se extrae por el opuesto, el primero que entró (101) es el primero que sale: disciplina FIFO (First In, First Out). Si insertásemos y extrajésemos por el mismo extremo (la cabeza), tendríamos una pila LIFO, y el primero en salir sería 103. El uso de dos punteros (cabeza y cola) es lo que permite que tanto la inserción por el final como la extracción por el principio sean O(1).
Cuestión 2 — Fugas de memoria
Sí queda memoria sin liberar. Se han hecho 3 llamadas a malloc (N1, N2, N3) y solo 2 a free (en los dos desencolar). Por tanto, al terminar main queda 1 nodo vivo: N3 (id=103, paginas=1), apuntado por c.cabeza y c.cola. Cuando main retorna, esos punteros desaparecen de la pila pero el bloque de N3 permanece reservado en el heap sin nadie que lo referencie: es una fuga de memoria (un detector como Valgrind reportaría "1 block still reachable / definitely lost").
Nota: en este programa concreto el SO recupera toda la memoria al terminar el proceso, pero en un servicio de larga duración (como el gestor de cola del taller) las fugas acumuladas agotan la memoria. Por eso hay que liberar explícitamente.
Función para vaciar la cola liberando todos los nodos restantes:
void vaciar(Cola *c) {
Nodo *actual = c->cabeza;
while (actual != NULL) {
Nodo *siguiente = actual->sig; /* (1) guardar sig ANTES de free */
free(actual); /* (2) liberar el nodo actual */
actual = siguiente; /* (3) avanzar con el guardado */
}
c->cabeza = NULL; /* estado coherente */
c->cola = NULL;
}
Orden correcto y por qué. La clave es la línea (1): hay que copiar actual->sig en una variable temporal antes de hacer free(actual). Si invirtiéramos el orden —free(actual); actual = actual->sig;— estaríamos leyendo el campo sig de un bloque ya liberado, lo que es un use-after-free (comportamiento indefinido: el bloque podría haber sido reutilizado, dando un puntero basura y un fallo de segmentación). Al guardar primero siguiente, el free posterior no nos impide avanzar. Finalmente se ponen cabeza y cola a NULL para que la cola quede en un estado consistente y reutilizable (vacía), evitando punteros colgantes.
Cuestión 3 — Detección de errores y versión defensiva
(a) desencolar sobre una cola vacía. Si c->cabeza == NULL, la línea Nodo *n = c->cabeza; asigna NULL a n, y a continuación int id = n->id; (línea 38 del código original) desreferencia un puntero nulo. Esto es un comportamiento indefinido; en la práctica produce un fallo de segmentación (segmentation fault / null pointer dereference). El error se manifiesta, por tanto, en la línea del acceso n->id.
(b) encolar no comprueba malloc. Si la memoria está agotada, malloc devuelve NULL y la siguiente línea n->id = id; vuelve a desreferenciar un puntero nulo → fallo de segmentación.
Efecto colateral sobre cola al desencolar el último nodo. Cuando se extrae el único nodo restante, c->cabeza pasa correctamente a NULL (porque n->sig == NULL), pero c->cola se queda apuntando al nodo recién liberado (puntero colgante / dangling pointer). En el siguiente encolar, la condición c->cabeza == NULL sí enrutaría por la rama then y la cosa se "arreglaría", pero dejar cola apuntando a memoria liberada es incorrecto y peligroso. Hay que ponerla también a NULL.
Versión defensiva de ambas funciones:
/* Devuelve 0 si reservó bien, -1 si no hubo memoria. */
int encolar(Cola *c, int id, int paginas) {
Nodo *n = malloc(sizeof(Nodo));
if (n == NULL) { /* (b) malloc puede fallar */
return -1; /* no se modifica la cola */
}
n->id = id;
n->paginas = paginas;
n->sig = NULL;
if (c->cabeza == NULL)
c->cabeza = n;
else
c->cola->sig = n;
c->cola = n;
return 0;
}
/* Devuelve el id atendido, o -1 (centinela) si la cola está vacía. */
int desencolar(Cola *c) {
if (c->cabeza == NULL) /* (a) no desreferenciar NULL */
return -1;
Nodo *n = c->cabeza;
int id = n->id;
c->cabeza = n->sig;
if (c->cabeza == NULL) /* se sacó el último nodo: */
c->cola = NULL; /* corrige el puntero colgante */
free(n);
return id;
}
Observaciones de justificación:
- En
encolar se comprueba malloc antes de tocar el nuevo nodo y antes de enlazarlo, de modo que un fallo de reserva deja la cola intacta (fuerte garantía de no corrupción).
- En
desencolar, el valor centinela -1 es válido aquí porque los identificadores de trabajo son positivos; en un diseño más estricto se devolvería el id por parámetro de salida y un código de error (int desencolar(Cola *c, int *id_out)).
- La guarda
if (c->cabeza == NULL) c->cola = NULL; se coloca antes del free (da igual respecto al free de n, porque no toca a n), garantizando que la cola vacía queda con ambos punteros a NULL.
Cuestión 4 — Variante con puntero doble (Nodo **)
void encolar(Cola *c, int id, int paginas) {
Nodo *n = malloc(sizeof(Nodo));
if (n == NULL) return;
n->id = id;
n->paginas = paginas;
n->sig = NULL;
/* pp apunta a la "casilla" donde debe escribirse la dirección de n */
Nodo **pp = (c->cabeza == NULL) ? &c->cabeza : &c->cola->sig;
*pp = n;
c->cola = n;
}
Si se quiere eliminar también ese ternario y razonar con un puntero doble que recorra hasta el final (variante sin campo cola cacheado):
void encolar_sin_cola(Nodo **cabeza, int id, int paginas) {
Nodo *n = malloc(sizeof(Nodo));
if (n == NULL) return;
n->id = id; n->paginas = paginas; n->sig = NULL;
Nodo **pp = cabeza; /* apunta a la casilla cabeza */
while (*pp != NULL) /* avanza mientras la casilla apunte */
pp = &(*pp)->sig; /* a un nodo: pasa a su casilla sig */
*pp = n; /* escribe en la primera casilla NULL */
}
Qué apunta el doble puntero en cada caso. pp no apunta a un nodo, sino a la casilla de memoria (el campo puntero) que hay que actualizar:
- Si la cola está vacía, la casilla a modificar es
c->cabeza, por lo que pp = &c->cabeza y *pp = n escribe el nodo en cabeza.
- Si la cola no está vacía, la casilla a modificar es el campo
sig del último nodo, por lo que pp = &c->cola->sig.
Por qué simplifica. El if/else del original existía solo porque "la casilla destino" cambia según haya o no nodos: en un caso se escribe en cabeza (un campo de la estructura Cola) y en otro en sig (un campo del último Nodo). Al trabajar con la dirección de esa casilla en lugar de con su contenido, ambos casos se unifican en una sola asignación *pp = n. El doble puntero captura la idea de "el sitio donde colgar el nuevo nodo", elimina la duplicación de ramas y reduce la posibilidad de olvidar uno de los dos casos.
Cuestión 5 — ¿Por qué estructura enlazada dinámica y no array estático?
| Criterio |
Cola enlazada dinámica |
Array estático de tamaño fijo |
encolar (insertar al final) |
O(1): se enlaza un nodo nuevo y se actualiza cola |
O(1) si solo se añade al final, pero requiere índices circulares para no desplazar |
desencolar (sacar del principio) |
O(1): se avanza cabeza y se libera el nodo |
O(n) si se desplazan todos los elementos un hueco; O(1) solo con buffer circular |
| Aprovechamiento de memoria |
Usa exactamente la memoria de los trabajos presentes; crece y decrece bajo demanda |
Reserva siempre el máximo previsto, aunque haya pocos trabajos → desperdicio |
| Capacidad / desbordamiento |
Limitada solo por la memoria del sistema; no hay tope artificial |
Tope fijo: al superarlo hay desbordamiento (buffer overflow) → trabajos perdidos o corrupción si no se controla |
| Coste por elemento |
Sobrecoste del puntero sig (8 bytes) y del malloc/free por nodo |
Sin punteros, mejor localidad de caché y acceso aleatorio O(1) |
Razonamiento. El enunciado dice que se desconoce a priori cuántos trabajos habrá, condición que descarta un array estático: habría que sobredimensionarlo (desperdiciando memoria si llegan pocos trabajos) y aun así existiría un techo a partir del cual se produciría un desbordamiento del array, con pérdida de trabajos o, peor, escritura fuera de límites y corrupción de memoria.
La cola enlazada ofrece tanto encolar como desencolar en O(1) constante —gracias a mantener los dos punteros cabeza y cola— sin necesidad de desplazar elementos, que es justo lo que penaliza a un array simple en la extracción por el frente (O(n)). Además ajusta el consumo de memoria a la carga real (crece y decrece con los trabajos), a costa de un pequeño sobrecoste de un puntero por nodo y de las llamadas a malloc/free. Para una cola de impresión de tamaño impredecible y vida prolongada, este equilibrio (escalabilidad + no desbordamiento) es claramente preferible. Si en cambio el número máximo de trabajos fuera conocido y pequeño, y primara la velocidad/localidad de caché, un buffer circular sobre array fijo sería una alternativa válida que también logra O(1) en ambas operaciones sin desplazamientos.