Este es el primero de una serie de ejemplos. Comenzamos con un escenario sencillo que consolida los conceptos básicos antes de avanzar hacia proyectos más complejos en temas posteriores.
Necesitamos un simulador pequeño para una sucursal que atiende clientes en orden de llegada. La cola debe registrar el número de ticket y un nombre breve. El objetivo es permitir tres operaciones desde consola:
Este ejemplo se centra en la cola circular de tamaño fijo, ideal para comenzar porque garantiza O(1) y evita asignaciones dinámicas.
La estructura circular reutiliza casilleros liberados y mantiene el orden de llegada de cada ticket.
Usaremos un arreglo de estructuras con tres campos: ticket, nombre y un flag de ocupación. La cola mantiene punteros frente y final, además de un contador para detectar estados vacío/lleno.
#define CAP_TURNOS 16
#define MAX_NOMBRE 32
typedef struct {
int ticket;
char nombre[MAX_NOMBRE];
} Cliente;
typedef struct {
Cliente datos[CAP_TURNOS];
int frente;
int final;
int cantidad;
int siguienteTicket;
} ColaTurnos;
void inicializar(ColaTurnos *cola) {
cola->frente = 0;
cola->final = 0;
cola->cantidad = 0;
cola->siguienteTicket = 1;
}
Las funciones agregarCliente, llamarCliente y mostrarCola encapsulan el flujo. Cada una devuelve un código para informar éxito o error (cola llena/vacía).
int agregarCliente(ColaTurnos *cola, const char *nombre) {
if (cola->cantidad == CAP_TURNOS) {
return 0;
}
Cliente *slot = &cola->datos[cola->final];
slot->ticket = cola->siguienteTicket++;
strncpy(slot->nombre, nombre, MAX_NOMBRE - 1);
slot->nombre[MAX_NOMBRE - 1] = '\0';
cola->final = (cola->final + 1) % CAP_TURNOS;
cola->cantidad++;
return slot->ticket;
}
int llamarCliente(ColaTurnos *cola, Cliente *cliente) {
if (cola->cantidad == 0) {
return 0;
}
*cliente = cola->datos[cola->frente];
cola->frente = (cola->frente + 1) % CAP_TURNOS;
cola->cantidad--;
return 1;
}
void mostrarCola(const ColaTurnos *cola) {
printf("Turnos pendientes (%d):\n", cola->cantidad);
for (int i = 0, idx = cola->frente; i < cola->cantidad; ++i, idx = (idx + 1) % CAP_TURNOS) {
printf(" #%d - %s\n", cola->datos[idx].ticket, cola->datos[idx].nombre);
}
}
El programa principal muestra un pequeño menú y ejecuta las operaciones según la entrada del usuario. Las entradas se validan para evitar desbordes.
void limpiarEntrada(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
int main(void) {
ColaTurnos cola;
inicializar(&cola);
int opcion;
char nombre[MAX_NOMBRE];
Cliente cliente;
do {
puts("\n1) Agregar cliente");
puts("2) Llamar siguiente");
puts("3) Mostrar cola");
puts("0) Salir");
printf("Opcion: ");
if (scanf("%d", &opcion) != 1) {
puts("Entrada invalida");
limpiarEntrada();
continue;
}
limpiarEntrada();
switch (opcion) {
case 1:
printf("Nombre: ");
if (!fgets(nombre, sizeof nombre, stdin)) break;
nombre[strcspn(nombre, "\n")] = '\0';
int ticket;
ticket = agregarCliente(&cola, nombre);
if (ticket == 0) {
puts("Cola llena, intenta mas tarde.");
} else {
printf("Ticket asignado: #%d\n", ticket);
}
break;
case 2:
if (llamarCliente(&cola, &cliente)) {
printf("Atendiendo #%d - %s\n", cliente.ticket, cliente.nombre);
} else {
puts("No hay clientes esperando.");
}
break;
case 3:
mostrarCola(&cola);
break;
case 0:
puts("Hasta luego");
break;
default:
puts("Opcion no valida");
}
} while (opcion != 0);
return 0;
}
Compila el siguiente archivo en CLion (o cualquier IDE) para probar el flujo sin necesidad de copiar fragmentos por separado:
#include <stdio.h>
#include <string.h>
#define CAP_TURNOS 16
#define MAX_NOMBRE 32
typedef struct {
int ticket;
char nombre[MAX_NOMBRE];
} Cliente;
typedef struct {
Cliente datos[CAP_TURNOS];
int frente;
int final;
int cantidad;
int siguienteTicket;
} ColaTurnos;
void inicializar(ColaTurnos *cola) {
cola->frente = 0;
cola->final = 0;
cola->cantidad = 0;
cola->siguienteTicket = 1;
}
int agregarCliente(ColaTurnos *cola, const char *nombre) {
if (cola->cantidad == CAP_TURNOS) {
return 0;
}
Cliente *slot = &cola->datos[cola->final];
slot->ticket = cola->siguienteTicket++;
strncpy(slot->nombre, nombre, MAX_NOMBRE - 1);
slot->nombre[MAX_NOMBRE - 1] = '\0';
cola->final = (cola->final + 1) % CAP_TURNOS;
cola->cantidad++;
return slot->ticket;
}
int llamarCliente(ColaTurnos *cola, Cliente *cliente) {
if (cola->cantidad == 0) {
return 0;
}
*cliente = cola->datos[cola->frente];
cola->frente = (cola->frente + 1) % CAP_TURNOS;
cola->cantidad--;
return 1;
}
void mostrarCola(const ColaTurnos *cola) {
printf("Turnos pendientes (%d):\n", cola->cantidad);
for (int i = 0, idx = cola->frente; i < cola->cantidad; ++i, idx = (idx + 1) % CAP_TURNOS) {
printf(" #%d - %s\n", cola->datos[idx].ticket, cola->datos[idx].nombre);
}
}
void limpiarEntrada(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
int main(void) {
ColaTurnos cola;
inicializar(&cola);
int opcion;
char nombre[MAX_NOMBRE];
Cliente cliente;
do {
puts("\n1) Agregar cliente");
puts("2) Llamar siguiente");
puts("3) Mostrar cola");
puts("0) Salir");
printf("Opcion: ");
if (scanf("%d", &opcion) != 1) {
puts("Entrada invalida");
limpiarEntrada();
continue;
}
limpiarEntrada();
switch (opcion) {
case 1: {
printf("Nombre: ");
if (!fgets(nombre, sizeof nombre, stdin)) break;
nombre[strcspn(nombre, "\n")] = '\0';
int ticket = agregarCliente(&cola, nombre);
if (ticket == 0) {
puts("Cola llena, intenta mas tarde.");
} else {
printf("Ticket asignado: #%d\n", ticket);
}
break;
}
case 2:
if (llamarCliente(&cola, &cliente)) {
printf("Atendiendo #%d - %s\n", cliente.ticket, cliente.nombre);
} else {
puts("No hay clientes esperando.");
}
break;
case 3:
mostrarCola(&cola);
break;
case 0:
puts("Hasta luego");
break;
default:
puts("Opcion no valida");
}
} while (opcion != 0);
return 0;
}
El programa es autocontenible y puede usarse como plantilla para futuros ejemplos con mayor complejidad.
La cola circular facilita mantener los tickets en orden incluso cuando la cantidad de clientes alcanza el límite.
En los siguientes ejemplos prácticos aumentaremos la complejidad incorporando hilos, prioridades y almacenamiento persistente.