Hoy vamos a ver un pequeño sistema de estacionamiento en el lenguaje de programación C, también conocido como ANSI C. Es un ejercicio resuelto. El mismo dice así:
Un estacionamiento posee 10 puestos para carros y 6 para moto. La empresa desea un software que le permita mantener el registro de los autos que entran y salen del estacionamiento para saber cuáles puestos quedan libres.
Consideraciones del sistema.
Veamos cómo hacer este ejercicio de estacionamiento en C.
Vamos a usar un arreglo de structs. Cada struct o estructura de C va a representar un lugar en el estacionamiento, y este tendrá varias propiedades como su estado (ocupado, desocupado), placas, hora de entrada, etcétera.
Las fechas (horas de entrada) las vamos a manejar como marcas de tiempo o timestamps que son un número entero, y después vamos a crear otra función que las va a formatear como fecha local.
Como vamos a tener todo como entero, sacar la diferencia de tiempo será muy fácil ya que tendremos la diferencia en segundos.
Iremos trabajando todo en un arreglo, y dividiremos el trabajo en funciones para poder hacerlo más sencillo.
Comencemos viendo la estructura de un lugar en el estacionamiento. Es un struct de C y se ve así:
struct LugarEstacionamiento
{
char placa[MAXIMA_LONGITUD_CADENA];
time_t entrada;
int estado;
char tipo;
int horas;
};
Las horas no son las horas que lleva estacionado el vehículo, sino las horas que el vehículo planea estar dentro del estacionamiento (para, más tarde, calcular el costo extra). Luego declaramos un arreglo y lo inicializamos:
void limpiarLugares(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int i;
for (i = 0; i < CANTIDAD_CARROS; i++)
{
strcpy(lugares[i].placa, "");
lugares[i].entrada = 0;
lugares[i].estado = DESOCUPADO;
lugares[i].tipo = TIPO_CARRO;
lugares[i].horas = 0;
}
for (i = CANTIDAD_CARROS; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
{
strcpy(lugares[i].placa, "");
lugares[i].entrada = 0;
lugares[i].estado = DESOCUPADO;
lugares[i].tipo = TIPO_MOTO;
lugares[i].horas = 0;
}
}
int main()
{
struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS];
limpiarLugares(lugares);
}
Como puedes ver, primero coloqué los carros y después las motos; todos pertenecen al mismo arreglo pero los diferencia el tipo.
La declaración está en la línea 26 y la limpieza está en la función que aparece al inicio.
Veamos cómo se registra un vehículo. Primero tenemos que verificar que el lugar sea correcto, que no esté ocupado y que no exista un vehículo con esas mismas placas.
Tenemos el menú donde solicitamos las placas, horas y lugar:
void menuAsignarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
char lecturaUsuario[MAXIMA_LONGITUD_CADENA] = "";
int lugar = 0;
int horas;
mostrarEstadoEstacionamiento(lugares);
while (1)
{
printf("Ingrese el lugar: ");
scanf("%d", &lugar);
if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
{
if (lugares[lugar - 1].estado == OCUPADO)
{
printf("Ocupado\n");
}
else
{
lugar = lugar - 1;
break;
}
}
else
{
printf("Numero no valido\n");
}
}
printf("Ingrese las horas que planea estar en el estacionamiento: ");
scanf("%d", &horas);
consumirNuevaLinea();
while (1)
{
printf("Ingrese las placas de");
if (lugares[lugar].tipo == TIPO_MOTO)
{
printf(" la moto: ");
}
else
{
printf(" el carro: ");
}
fgets(lecturaUsuario, MAXIMA_LONGITUD_CADENA, stdin);
lecturaUsuario[strcspn(lecturaUsuario, "\r\n")] = 0;
if (existeVehiculoConPlacaYTipo(lugares, lecturaUsuario, lugares[lugar].tipo))
{
printf("Ya existe un vehiculo del mismo tipo y con las mismas placas en el sistema\n");
}
else
{
break;
}
}
marcarEntradaVehiculo(lugares, lugar, lecturaUsuario, horas);
}
Y después invocamos a la función que modifica el arreglo, pasándole el índice del elemento que tiene que modificar (debido a que los arreglos se pasan por referencia, va a modificar al arreglo original):
void marcarEntradaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice, char placa[MAXIMA_LONGITUD_CADENA], int horas)
{
lugares[indice].estado = OCUPADO;
strncpy(lugares[indice].placa, placa, MAXIMA_LONGITUD_CADENA);
lugares[indice].entrada = time(NULL);
lugares[indice].horas = horas;
}
Simplemente le ponemos el estado ocupado y marcamos la hora de entrada. Para obtenerla invocamos a time
pasándole NULL
, de modo que nos devuelve el timestamp de la hora actual.
Para solicitar el lugar al que ingresa el vehículo, debemos mostrar el estado de los lugares, quién los ocupa y desde cuándo.
El listado queda así:
void mostrarEstadoEstacionamiento(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int i;
for (i = 0; i < CANTIDAD_CARROS; i++)
{
printf("#%d [CARRO] ", i + 1);
if (lugares[i].estado == OCUPADO)
{
printf("Ocupado por %s desde ", lugares[i].placa);
imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
}
else
{
printf("Desocupado");
}
printf("\n");
}
for (; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
{
printf("#%d [MOTO] ", i + 1);
if (lugares[i].estado == OCUPADO)
{
printf("Ocupado por %s desde ", lugares[i].placa);
imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
}
else
{
printf("Desocupado");
}
printf("\n");
}
}
Lo que hace es listar todos los lugares y su estado junto con la fecha de entrada (aquí estamos convirtiendo el timestamp a fecha). Así de simple.
Para terminar con nuestro programa en C para gestionar un estacionamiento, veamos cómo marcar la salida de un vehículo. Aquí es en donde se calcula el tiempo transcurrido, costos, etcétera.
Primero un menú para solicitar el lugar y verificar si ese lugar está ocupado:
void menuSacarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int lugar = 0;
while (1)
{
printf("Ingrese el lugar: ");
scanf("%d", &lugar);
if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
{
if (lugares[lugar - 1].estado == DESOCUPADO)
{
printf("No hay vehiculo estacionado en este lugar\n");
}
else
{
lugar = lugar - 1;
break;
}
}
else
{
printf("Numero no valido\n");
}
}
marcarSalidaVehiculo(lugares, lugar);
}
Luego tenemos a la función que invoca a otras 2: la que realmente hace el cálculo y la que limpia el lugar.
void marcarSalidaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice)
{
calcularEImprimirInformacionSalida(lugares[indice]);
limpiarLugar(&lugares[indice]);
}
void calcularEImprimirInformacionSalida(struct LugarEstacionamiento lugarEstacionamiento)
{
int horasSegunVehiculo = lugarEstacionamiento.horas;
double total = 0;
double granTotal = 0;
double totalExcedente = 0;
if (lugarEstacionamiento.tipo == TIPO_CARRO)
{
total = horasSegunVehiculo * VALOR_HORA_CARRO;
}
else
{
total = horasSegunVehiculo * VALOR_HORA_MOTO;
}
time_t ahora = time(NULL);
time_t entrada = lugarEstacionamiento.entrada;
// Nota: si se quieren hacer pruebas con otras fechas, hay que
// cambiar el timestamp a la fecha deseada. Por ejemplo, la siguiente
// línea apunta al 10 de marzo de 2022 a las 11:00:00
// time_t entrada = 1646931600;
time_t diferencia = ahora - entrada;
printf("===========================\n");
printf("Entrada: ");
imprimirFechaAPartirDeTimestamp(entrada);
printf("\n");
printf("Salida: ");
imprimirFechaAPartirDeTimestamp(ahora);
printf("\n");
printf("Horas solicitadas: %d\n", horasSegunVehiculo);
unsigned long long int horas = diferencia / 60 / 60;
diferencia -= 60 * 60 * horas;
unsigned long long int minutos = diferencia / 60;
diferencia -= 60 * minutos;
int conTiempoExtra = 0;
if (horas >= horasSegunVehiculo && (((horas - horasSegunVehiculo) * 60) + minutos) >= MINUTOS_EXTRA_PARA_RECARGO)
{
conTiempoExtra = 1;
totalExcedente = (((horas - horasSegunVehiculo) * 60) + minutos) * VALOR_POR_MINUTO;
}
granTotal = total + totalExcedente;
printf("Tiempo dentro de estacionamiento: ");
printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas, minutos, diferencia);
if (conTiempoExtra)
{
printf("Tiempo extra: ");
printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas - horasSegunVehiculo, minutos, diferencia);
printf("Pago por tiempo extra: %lf\n", totalExcedente);
}
printf("Placa: %s\n", lugarEstacionamiento.placa);
printf("Pago por horas: %0.2lf\n", total);
printf("Total: %0.2lf\n", granTotal);
printf("===========================\n");
}
void limpiarLugar(struct LugarEstacionamiento *lugarEstacionamiento)
{
lugarEstacionamiento->entrada = 0;
lugarEstacionamiento->estado = DESOCUPADO;
lugarEstacionamiento->horas = 0;
strcpy(lugarEstacionamiento->placa, "");
}
Por favor nota que en el caso de la limpieza del lugar estamos recibiendo un apuntador al elemento del arreglo de los lugares. Esto es porque no estamos recibiendo el arreglo completo.
Toda la magia y operaciones suceden en la función calcularEImprimirInformacionSalida
. Ahí calculamos la diferencia y cobramos las horas que el vehículo solicitó al entrar, sin importar si alcanzó ese tiempo o no.
Luego nos fijamos en el tiempo extra y en caso de que exista multiplicamos los minutos extra (tomando en cuenta también las horas, por ello la multiplicación por 60) por el costo de cada minuto extra.
Arriba te mostré las funciones más importantes, pero no es todo el código. El código completo queda así:
/*
https://parzibyte.me/blog
*/#include <stdio.h>
#include <time.h>
#include <string.h>
#define CANTIDAD_MOTOS 6
#define CANTIDAD_CARROS 10
#define MAXIMA_LONGITUD_CADENA 1000
#define OCUPADO 1
#define DESOCUPADO 0
#define TIPO_MOTO 'M'
#define TIPO_CARRO 'C'
#define VALOR_HORA_CARRO 3000
#define VALOR_HORA_MOTO 1000
#define MINUTOS_EXTRA_PARA_RECARGO 2
#define VALOR_POR_MINUTO 200
void imprimirFechaAPartirDeTimestamp(time_t tiempo)
{
struct tm *tm = localtime(&tiempo);
printf("%02d/%02d/%02d %02d:%02d:%02d",
tm->tm_mday, tm->tm_mon + 1,
tm->tm_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
}
void consumirNuevaLinea(void)
{
int c;
do
{
c = getchar();
} while (c != EOF && c != '\n');
}
struct LugarEstacionamiento
{
char placa[MAXIMA_LONGITUD_CADENA];
time_t entrada;
int estado;
char tipo;
int horas;
};
void limpiarLugares(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int i;
for (i = 0; i < CANTIDAD_CARROS; i++)
{
strcpy(lugares[i].placa, "");
lugares[i].entrada = 0;
lugares[i].estado = DESOCUPADO;
lugares[i].tipo = TIPO_CARRO;
lugares[i].horas = 0;
}
for (i = CANTIDAD_CARROS; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
{
strcpy(lugares[i].placa, "");
lugares[i].entrada = 0;
lugares[i].estado = DESOCUPADO;
lugares[i].tipo = TIPO_MOTO;
lugares[i].horas = 0;
}
}
void mostrarEstadoEstacionamiento(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int i;
for (i = 0; i < CANTIDAD_CARROS; i++)
{
printf("#%d [CARRO] ", i + 1);
if (lugares[i].estado == OCUPADO)
{
printf("Ocupado por %s desde ", lugares[i].placa);
imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
}
else
{
printf("Desocupado");
}
printf("\n");
}
for (; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
{
printf("#%d [MOTO] ", i + 1);
if (lugares[i].estado == OCUPADO)
{
printf("Ocupado por %s desde ", lugares[i].placa);
imprimirFechaAPartirDeTimestamp(lugares[i].entrada);
}
else
{
printf("Desocupado");
}
printf("\n");
}
}
void marcarEntradaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice, char placa[MAXIMA_LONGITUD_CADENA], int horas)
{
lugares[indice].estado = OCUPADO;
strncpy(lugares[indice].placa, placa, MAXIMA_LONGITUD_CADENA);
lugares[indice].entrada = time(NULL);
lugares[indice].horas = horas;
}
void limpiarLugar(struct LugarEstacionamiento *lugarEstacionamiento)
{
lugarEstacionamiento->entrada = 0;
lugarEstacionamiento->estado = DESOCUPADO;
lugarEstacionamiento->horas = 0;
strcpy(lugarEstacionamiento->placa, "");
}
void calcularEImprimirInformacionSalida(struct LugarEstacionamiento lugarEstacionamiento)
{
int horasSegunVehiculo = lugarEstacionamiento.horas;
double total = 0;
double granTotal = 0;
double totalExcedente = 0;
if (lugarEstacionamiento.tipo == TIPO_CARRO)
{
total = horasSegunVehiculo * VALOR_HORA_CARRO;
}
else
{
total = horasSegunVehiculo * VALOR_HORA_MOTO;
}
time_t ahora = time(NULL);
time_t entrada = lugarEstacionamiento.entrada;
// Nota: si se quieren hacer pruebas con otras fechas, hay que
// cambiar el timestamp a la fecha deseada. Por ejemplo, la siguiente
// línea apunta al 10 de marzo de 2022 a las 11:00:00
// time_t entrada = 1646931600;
time_t diferencia = ahora - entrada;
printf("===========================\n");
printf("Entrada: ");
imprimirFechaAPartirDeTimestamp(entrada);
printf("\n");
printf("Salida: ");
imprimirFechaAPartirDeTimestamp(ahora);
printf("\n");
printf("Horas solicitadas: %d\n", horasSegunVehiculo);
unsigned long long int horas = diferencia / 60 / 60;
diferencia -= 60 * 60 * horas;
unsigned long long int minutos = diferencia / 60;
diferencia -= 60 * minutos;
int conTiempoExtra = 0;
if (horas >= horasSegunVehiculo && (((horas - horasSegunVehiculo) * 60) + minutos) >= MINUTOS_EXTRA_PARA_RECARGO)
{
conTiempoExtra = 1;
totalExcedente = (((horas - horasSegunVehiculo) * 60) + minutos) * VALOR_POR_MINUTO;
}
granTotal = total + totalExcedente;
printf("Tiempo dentro de estacionamiento: ");
printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas, minutos, diferencia);
if (conTiempoExtra)
{
printf("Tiempo extra: ");
printf("%llu hora(s) %llu minuto(s) %llu segundo(s)\n", horas - horasSegunVehiculo, minutos, diferencia);
printf("Pago por tiempo extra: %lf\n", totalExcedente);
}
printf("Placa: %s\n", lugarEstacionamiento.placa);
printf("Pago por horas: %0.2lf\n", total);
printf("Total: %0.2lf\n", granTotal);
printf("===========================\n");
}
void marcarSalidaVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], int indice)
{
calcularEImprimirInformacionSalida(lugares[indice]);
limpiarLugar(&lugares[indice]);
}
int existeVehiculoConPlacaYTipo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS], char placa[MAXIMA_LONGITUD_CADENA], char tipo)
{
int i;
for (i = 0; i < CANTIDAD_CARROS + CANTIDAD_MOTOS; i++)
{
if (lugares[i].tipo == tipo && strcmp(lugares[i].placa, placa) == 0)
{
return 1;
}
}
return 0;
}
void menuAsignarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
char lecturaUsuario[MAXIMA_LONGITUD_CADENA] = "";
int lugar = 0;
int horas;
mostrarEstadoEstacionamiento(lugares);
while (1)
{
printf("Ingrese el lugar: ");
scanf("%d", &lugar);
if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
{
if (lugares[lugar - 1].estado == OCUPADO)
{
printf("Ocupado\n");
}
else
{
lugar = lugar - 1;
break;
}
}
else
{
printf("Numero no valido\n");
}
}
printf("Ingrese las horas que planea estar en el estacionamiento: ");
scanf("%d", &horas);
consumirNuevaLinea();
while (1)
{
printf("Ingrese las placas de");
if (lugares[lugar].tipo == TIPO_MOTO)
{
printf(" la moto: ");
}
else
{
printf(" el carro: ");
}
fgets(lecturaUsuario, MAXIMA_LONGITUD_CADENA, stdin);
lecturaUsuario[strcspn(lecturaUsuario, "\r\n")] = 0;
if (existeVehiculoConPlacaYTipo(lugares, lecturaUsuario, lugares[lugar].tipo))
{
printf("Ya existe un vehiculo del mismo tipo y con las mismas placas en el sistema\n");
}
else
{
break;
}
}
marcarEntradaVehiculo(lugares, lugar, lecturaUsuario, horas);
}
void menuSacarVehiculo(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int lugar = 0;
while (1)
{
printf("Ingrese el lugar: ");
scanf("%d", &lugar);
if (lugar > 0 && lugar <= CANTIDAD_CARROS + CANTIDAD_MOTOS)
{
if (lugares[lugar - 1].estado == DESOCUPADO)
{
printf("No hay vehiculo estacionado en este lugar\n");
}
else
{
lugar = lugar - 1;
break;
}
}
else
{
printf("Numero no valido\n");
}
}
marcarSalidaVehiculo(lugares, lugar);
}
void menu(struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS])
{
int eleccion = 0;
while (eleccion != 4)
{
printf("1. Registrar entrada\n2. Registrar salida\n3. Ver estado de estacionamiento\n4. Salir\nElige: ");
scanf("%d", &eleccion);
if (eleccion == 1)
{
menuAsignarVehiculo(lugares);
}
else if (eleccion == 2)
{
menuSacarVehiculo(lugares);
}
else if (eleccion == 3)
{
mostrarEstadoEstacionamiento(lugares);
}
}
}
int main()
{
struct LugarEstacionamiento lugares[CANTIDAD_CARROS + CANTIDAD_MOTOS];
limpiarLugares(lugares);
menu(lugares);
}
Yo lo he probado con el compilador GCC y Dev-C++; en ambos lugares funciona correctamente aunque no recomiendo usar Dev-C++.
Para terminar te dejo con más tutoriales de C en mi blog.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.