Técnicas de Simulación de Estructuras de Control
Alternativas y patrones para simular comportamientos de bucles
Introducción¶
En ciertas situaciones de programación, podés encontrarte con la necesidad de
simular comportamientos de estructuras de control que no están disponibles en el
lenguaje que estás usando, o que querés evitar por razones de estilo o
compatibilidad. Este apunte explora técnicas para simular diversas estructuras
de control usando construcciones más básicas, con especial énfasis en la
simulación de do...while
usando while
y otras alternativas.
La comprensión de estas técnicas no solo es útil para casos específicos donde las estructuras originales no están disponibles, sino que también proporciona una comprensión más profunda de cómo funcionan internamente los bucles y las estructuras de control.
Simulación de do...while
con while
¶
Método 1: Bucle Infinito con break
¶
La técnica más directa para simular un do...while
es usar un bucle while(1)
con una condición de salida explícita:
1 2 3 4 5 6 7 8 9 10 11 12 13
// Comportamiento deseado (do...while): // do { // // código del bucle // } while (condicion); // Simulación equivalente: while (1) { // código del bucle if (!condicion) { break; } }
Simulación básica de do...while
Ejemplo Práctico: Validación de Entrada¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#include <stdio.h> #include <stdbool.h> int main() { int clave; bool clave_valida = false; printf("=== Sistema de Autenticación ===\n"); while (1) { printf("Ingresá la clave (123): "); scanf("%d", &clave); if (clave == 123) { clave_valida = true; break; } else { printf("Clave incorrecta. Intentá nuevamente.\n"); } } printf("Acceso concedido.\n"); return 0; }
Validación de entrada con simulación de do...while
Método 2: Variable de Control Booleana¶
Una alternativa más explícita es usar una variable booleana para controlar la continuación del bucle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include <stdbool.h> int validar_entrada() { bool continuar = true; int numero; while (continuar) { printf("Ingresá un número entre 1 y 10: "); scanf("%d", &numero); if (numero >= 1 && numero <= 10) { printf("Número válido: %d\n", numero); continuar = false; // Salir del bucle } else { printf("Número fuera de rango. Intentá nuevamente.\n"); } } return numero; }
Simulación con variable de control
Método 3: Función con Retorno Temprano¶
Para casos más complejos, podés encapsular la lógica en una función y usar
return
para salir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#include <stdio.h> #include <string.h> void procesar_comandos() { char comando[50]; printf("Intérprete de comandos (escribí 'salir' para terminar)\n"); while (1) { printf(">>> "); fgets(comando, sizeof(comando), stdin); // Remover salto de línea comando[strcspn(comando, "\n")] = 0; if (strcmp(comando, "salir") == 0) { printf("¡Hasta luego!\n"); return; } if (strcmp(comando, "ayuda") == 0) { printf("Comandos disponibles: ayuda, version, salir\n"); } else if (strcmp(comando, "version") == 0) { printf("Intérprete v1.0\n"); } else if (strlen(comando) > 0) { printf("Comando desconocido: '%s'\n", comando); } } }
Simulación con función y return
Simulación de Otras Estructuras de Control¶
Simulación de for
con while
¶
En algunos contextos educativos o de depuración, puede ser útil convertir bucles
for
a while
:
1 2 3 4 5 6 7 8 9 10 11 12 13
// Bucle for original: // for (int i = 0; i < n; i++) { // // código del bucle // } // Simulación equivalente con while: { int i = 0; // Inicialización while (i < n) { // Condición // código del bucle i++; // Incremento } }
Conversión de for a while
Simulación de switch
con if-else
¶
Para casos donde switch
no está disponible o es preferible evitarlo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
typedef enum { OPCION_NUEVA_PARTIDA, OPCION_CARGAR_PARTIDA, OPCION_CONFIGURACION, OPCION_SALIR } opcion_menu_t; void procesar_opcion_menu(opcion_menu_t opcion) { if (opcion == OPCION_NUEVA_PARTIDA) { printf("Iniciando nueva partida...\n"); } else if (opcion == OPCION_CARGAR_PARTIDA) { printf("Cargando partida guardada...\n"); } else if (opcion == OPCION_CONFIGURACION) { printf("Abriendo configuración...\n"); } else if (opcion == OPCION_SALIR) { printf("Saliendo del juego...\n"); } else { printf("Opción inválida: %d\n", opcion); } }
Simulación de switch con if-else encadenados
Patrones Avanzados de Simulación¶
Máquina de Estados sin switch
¶
Para sistemas complejos que requieren máquinas de estado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
#include <stdio.h> typedef enum { ESTADO_MENU, ESTADO_JUGANDO, ESTADO_PAUSA, ESTADO_GAME_OVER, ESTADO_SALIR, ESTADO_MAX } estado_juego_t; typedef struct { estado_juego_t estado_actual; int puntuacion; int vidas; } contexto_juego_t; // Prototipos de funciones de estado void manejar_estado_menu(contexto_juego_t *ctx); void manejar_estado_jugando(contexto_juego_t *ctx); void manejar_estado_pausa(contexto_juego_t *ctx); void manejar_estado_game_over(contexto_juego_t *ctx); // Tabla de funciones para simular switch typedef void (*funcion_estado_t)(contexto_juego_t *); static funcion_estado_t tabla_estados[ESTADO_MAX] = { [ESTADO_MENU] = manejar_estado_menu, [ESTADO_JUGANDO] = manejar_estado_jugando, [ESTADO_PAUSA] = manejar_estado_pausa, [ESTADO_GAME_OVER] = manejar_estado_game_over }; void ejecutar_maquina_estados(contexto_juego_t *ctx) { while (ctx->estado_actual != ESTADO_SALIR) { if (ctx->estado_actual < ESTADO_MAX && tabla_estados[ctx->estado_actual]) { tabla_estados[ctx->estado_actual](ctx); } else { fprintf(stderr, "Estado inválido: %d\n", ctx->estado_actual); ctx->estado_actual = ESTADO_SALIR; } } } void manejar_estado_menu(contexto_juego_t *ctx) { printf("=== MENÚ PRINCIPAL ===\n"); printf("1. Jugar\n2. Salir\n"); int opcion; printf("Selecciona opción: "); scanf("%d", &opcion); if (opcion == 1) { ctx->estado_actual = ESTADO_JUGANDO; ctx->puntuacion = 0; ctx->vidas = 3; } else if (opcion == 2) { ctx->estado_actual = ESTADO_SALIR; } } void manejar_estado_jugando(contexto_juego_t *ctx) { printf("Jugando... Puntuación: %d, Vidas: %d\n", ctx->puntuacion, ctx->vidas); // Simular eventos del juego ctx->puntuacion += 10; if (ctx->puntuacion >= 100) { printf("¡Ganaste!\n"); ctx->estado_actual = ESTADO_GAME_OVER; } else { // Continuar jugando o ir a menú printf("Presiona 1 para continuar, 2 para ir al menú: "); int opcion; scanf("%d", &opcion); if (opcion == 2) { ctx->estado_actual = ESTADO_MENU; } } } void manejar_estado_pausa(contexto_juego_t *ctx) { printf("Juego en pausa. Presiona cualquier tecla para continuar...\n"); getchar(); ctx->estado_actual = ESTADO_JUGANDO; } void manejar_estado_game_over(contexto_juego_t *ctx) { printf("=== GAME OVER ===\n"); printf("Puntuación final: %d\n", ctx->puntuacion); ctx->estado_actual = ESTADO_MENU; }
Máquina de estados con tabla de funciones
Simulación de Bucles Anidados con Funciones¶
Para evitar bucles anidados complejos que violan la regla de claridad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
#include <stdbool.h> #define FILAS 3 #define COLUMNAS 4 // En lugar de bucles anidados complejos: void procesar_matriz_compleja_anidado(int matriz[FILAS][COLUMNAS]) { for (int i = 0; i < FILAS; i++) { for (int j = 0; j < COLUMNAS; j++) { if (matriz[i][j] > 0) { for (int k = 0; k < matriz[i][j]; k++) { // Lógica compleja aquí... printf("Procesando elemento [%d][%d], iteración %d\n", i, j, k); } } } } } // Simulación con funciones separadas: void procesar_elemento(int fila, int columna, int valor) { if (valor <= 0) return; for (int k = 0; k < valor; k++) { printf("Procesando elemento [%d][%d], iteración %d\n", fila, columna, k); } } void procesar_fila(int matriz[COLUMNAS], int fila) { for (int j = 0; j < COLUMNAS; j++) { procesar_elemento(fila, j, matriz[j]); } } void procesar_matriz_compleja_funcional(int matriz[FILAS][COLUMNAS]) { for (int i = 0; i < FILAS; i++) { procesar_fila(matriz[i], i); } }
Desensamblado de bucles anidados
Técnicas para Evitar goto
¶
Aunque goto
puede ser útil en casos específicos, su uso puede complicar el
flujo del programa:
Método 1: Funciones de Limpieza¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
#include <stdio.h> #include <stdlib.h> // Problemático con goto: // int procesar_archivo_con_goto(const char *nombre) { // FILE *archivo = NULL; // char *buffer = NULL; // int resultado = -1; // // archivo = fopen(nombre, "r"); // if (!archivo) goto cleanup; // // buffer = malloc(1024); // if (!buffer) goto cleanup; // // // Procesar archivo... // resultado = 0; // // cleanup: // if (buffer) free(buffer); // if (archivo) fclose(archivo); // return resultado; // } // Alternativa sin goto: typedef struct { FILE *archivo; char *buffer; int resultado; } recursos_t; void limpiar_recursos(recursos_t *recursos) { if (recursos->buffer) { free(recursos->buffer); recursos->buffer = NULL; } if (recursos->archivo) { fclose(recursos->archivo); recursos->archivo = NULL; } } int procesar_archivo_sin_goto(const char *nombre) { recursos_t recursos = {0}; recursos.archivo = fopen(nombre, "r"); if (!recursos.archivo) { limpiar_recursos(&recursos); return -1; } recursos.buffer = malloc(1024); if (!recursos.buffer) { limpiar_recursos(&recursos); return -1; } // Procesar archivo... printf("Procesando archivo: %s\n", nombre); recursos.resultado = 0; limpiar_recursos(&recursos); return recursos.resultado; }
Evitar goto con funciones de limpieza
Método 2: Banderas de Estado¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
#include <stdbool.h> int procesar_datos_complejos() { bool error_ocurrido = false; int resultado = 0; // Paso 1 if (!error_ocurrido) { printf("Ejecutando paso 1...\n"); if (/* condición de error */) { error_ocurrido = true; resultado = -1; } } // Paso 2 if (!error_ocurrido) { printf("Ejecutando paso 2...\n"); if (/* otra condición de error */) { error_ocurrido = true; resultado = -2; } } // Paso 3 if (!error_ocurrido) { printf("Ejecutando paso 3...\n"); // Lógica final resultado = 1; // éxito } if (error_ocurrido) { printf("Error durante el procesamiento: código %d\n", resultado); } else { printf("Procesamiento completado exitosamente.\n"); } return resultado; }
Uso de banderas en lugar de goto
Consideraciones de Rendimiento¶
Impacto en la Optimización del Compilador¶
Las simulaciones pueden afectar las optimizaciones automáticas del compilador:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#include <stdio.h> // Versión optimizable (compilador puede desenrollar el bucle) void bucle_simple_optimizable(int n) { for (int i = 0; i < n; i++) { printf("%d ", i); } } // Versión menos optimizable (lógica más compleja para el compilador) void bucle_simulado_complejo(int n) { int i = 0; bool continuar = true; while (continuar) { printf("%d ", i); i++; if (i >= n) { continuar = false; } } } // Versión equilibrada (clara para humanos, optimizable para compilador) void bucle_simulado_optimizable(int n) { int i = 0; while (i < n) { printf("%d ", i); i++; } }
Consideraciones de rendimiento
Medición de Rendimiento¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#include <time.h> void benchmark_bucles(int iteraciones) { clock_t inicio, fin; // Benchmark bucle for nativo inicio = clock(); for (int i = 0; i < iteraciones; i++) { // Operación trivial para evitar optimización completa volatile int temp = i * 2; } fin = clock(); double tiempo_for = ((double)(fin - inicio)) / CLOCKS_PER_SEC; // Benchmark simulación while inicio = clock(); int i = 0; while (i < iteraciones) { volatile int temp = i * 2; i++; } fin = clock(); double tiempo_while = ((double)(fin - inicio)) / CLOCKS_PER_SEC; printf("Rendimiento para %d iteraciones:\n", iteraciones); printf(" Bucle for: %.6f segundos\n", tiempo_for); printf(" Simulación while: %.6f segundos\n", tiempo_while); printf(" Diferencia: %.2f%%\n", ((tiempo_while - tiempo_for) / tiempo_for) * 100); }
Comparación de rendimiento entre técnicas
Mejores Prácticas para Simulación¶
1. Prioridad de Claridad¶
Siempre preferí la construcción más clara y natural del lenguaje:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Innecesariamente complejo void ejemplo_malo() { int i = 0; while (1) { if (i >= 10) break; printf("%d\n", i); i++; } } // Claro y directo void ejemplo_bueno() { for (int i = 0; i < 10; i++) { printf("%d\n", i); } }
Priorizar claridad sobre simulación
2. Documentación de Intención¶
Cuando uses simulación, documentá el porqué:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/** * Simula comportamiento do...while para mantener compatibilidad * con compiladores que no soportan C99. * * Comportamiento equivalente: * do { * procesar_entrada(&entrada); * } while (!entrada.es_valida); */ void validar_entrada_compatible() { entrada_t entrada; while (1) { procesar_entrada(&entrada); if (entrada.es_valida) { break; } } }
Documentación de intención en simulaciones
3. Pruebas de Equivalencia¶
Verificá que la simulación sea equivalente al comportamiento original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
void test_equivalencia_do_while() { int contador_original = 0; int contador_simulado = 0; // Comportamiento original (do...while) int i = 0; do { contador_original++; i++; } while (i < 5); // Simulación i = 0; while (1) { contador_simulado++; i++; if (!(i < 5)) { break; } } // Verificar equivalencia assert(contador_original == contador_simulado); printf("Simulación verificada: %d == %d iteraciones\n", contador_original, contador_simulado); }
Verificación de equivalencia
Casos de Uso Específicos¶
1. Menús Interactivos¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
typedef enum { MENU_OPCION_NUEVA_TAREA = 1, MENU_OPCION_LISTAR_TAREAS, MENU_OPCION_COMPLETAR_TAREA, MENU_OPCION_SALIR } opcion_menu_t; void mostrar_menu() { printf("\n=== GESTOR DE TAREAS ===\n"); printf("1. Nueva tarea\n"); printf("2. Listar tareas\n"); printf("3. Completar tarea\n"); printf("4. Salir\n"); printf("Selecciona opción: "); } void ejecutar_menu_principal() { int opcion; bool salir = false; while (!salir) { mostrar_menu(); if (scanf("%d", &opcion) != 1) { // Limpiar buffer en caso de entrada inválida while (getchar() != '\n'); printf("Entrada inválida. Usa números del 1 al 4.\n"); continue; } if (opcion == MENU_OPCION_NUEVA_TAREA) { printf("Creando nueva tarea...\n"); } else if (opcion == MENU_OPCION_LISTAR_TAREAS) { printf("Listando tareas...\n"); } else if (opcion == MENU_OPCION_COMPLETAR_TAREA) { printf("Completando tarea...\n"); } else if (opcion == MENU_OPCION_SALIR) { printf("¡Hasta luego!\n"); salir = true; } else { printf("Opción inválida: %d\n", opcion); } } }
Menú interactivo robusto
2. Procesamiento de Archivos con Manejo de Errores¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
#include <errno.h> #include <string.h> typedef enum { RESULTADO_EXITO, RESULTADO_ERROR_APERTURA, RESULTADO_ERROR_LECTURA, RESULTADO_ERROR_PROCESAMIENTO } resultado_procesamiento_t; resultado_procesamiento_t procesar_archivo_robusto(const char *ruta) { FILE *archivo = NULL; char linea[256]; int linea_numero = 0; bool error_encontrado = false; resultado_procesamiento_t resultado = RESULTADO_EXITO; // Intentar abrir archivo archivo = fopen(ruta, "r"); if (!archivo) { printf("Error abriendo '%s': %s\n", ruta, strerror(errno)); return RESULTADO_ERROR_APERTURA; } printf("📖 Procesando archivo: %s\n", ruta); // Simular do...while para procesar líneas while (1) { if (!fgets(linea, sizeof(linea), archivo)) { if (feof(archivo)) { break; // Fin de archivo normal } else { printf("Error leyendo línea %d: %s\n", linea_numero + 1, strerror(errno)); resultado = RESULTADO_ERROR_LECTURA; error_encontrado = true; break; } } linea_numero++; // Simular procesamiento de línea if (strlen(linea) == 0) { continue; // Saltar líneas vacías } // Remover salto de línea linea[strcspn(linea, "\n")] = 0; printf(" Línea %d: %s\n", linea_numero, linea); // Simular condición de error en procesamiento if (strstr(linea, "ERROR") != NULL) { printf("Error en contenido, línea %d: %s\n", linea_numero, linea); resultado = RESULTADO_ERROR_PROCESAMIENTO; error_encontrado = true; break; } } fclose(archivo); if (!error_encontrado) { printf("Archivo procesado exitosamente: %d líneas\n", linea_numero); } return resultado; }
Procesamiento robusto de archivos
Ejercicios¶
Solution to Exercise simulacion-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#include <stdio.h> #include <stdbool.h> typedef enum { OPERACION_SUMA = 1, OPERACION_RESTA, OPERACION_MULTIPLICACION, OPERACION_DIVISION, OPERACION_SALIR } operacion_t; void mostrar_menu_calculadora() { printf("\n=== CALCULADORA SIMPLE ===\n"); printf("1. Suma (+)\n"); printf("2. Resta (-)\n"); printf("3. Multiplicación (*)\n"); printf("4. División (/)\n"); printf("5. Salir\n"); printf("Selecciona operación: "); } bool obtener_numeros(double *a, double *b) { printf("Ingresa el primer número: "); if (scanf("%lf", a) != 1) { printf("Entrada inválida para el primer número\n"); while (getchar() != '\n'); // Limpiar buffer return false; } printf("Ingresa el segundo número: "); if (scanf("%lf", b) != 1) { printf("Entrada inválida para el segundo número\n"); while (getchar() != '\n'); // Limpiar buffer return false; } return true; } void ejecutar_calculadora() { int opcion; double num1, num2, resultado; bool continuar = true; printf("¡Bienvenido a la calculadora!\n"); // Simulación de do...while usando while(1) + break while (continuar) { mostrar_menu_calculadora(); if (scanf("%d", &opcion) != 1) { printf("Entrada inválida. Usa números del 1 al 5.\n"); while (getchar() != '\n'); // Limpiar buffer continue; } if (opcion == OPERACION_SALIR) { printf("¡Gracias por usar la calculadora!\n"); continuar = false; continue; } if (opcion < OPERACION_SUMA || opcion > OPERACION_DIVISION) { printf("Opción inválida: %d\n", opcion); continue; } if (!obtener_numeros(&num1, &num2)) { continue; } // Procesar operación bool operacion_valida = true; if (opcion == OPERACION_SUMA) { resultado = num1 + num2; printf("%.2f + %.2f = %.2f\n", num1, num2, resultado); } else if (opcion == OPERACION_RESTA) { resultado = num1 - num2; printf("%.2f - %.2f = %.2f\n", num1, num2, resultado); } else if (opcion == OPERACION_MULTIPLICACION) { resultado = num1 * num2; printf("%.2f * %.2f = %.2f\n", num1, num2, resultado); } else if (opcion == OPERACION_DIVISION) { if (num2 == 0.0) { printf("Error: División por cero no está permitida\n"); operacion_valida = false; } else { resultado = num1 / num2; printf("%.2f / %.2f = %.2f\n", num1, num2, resultado); } } if (operacion_valida) { printf("Operación completada\n"); } } } int main() { ejecutar_calculadora(); return 0; }
Solution to Exercise simulacion-2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
#include <stdio.h> #include <stdbool.h> #include <string.h> typedef enum { ESTADO_INICIO, ESTADO_VALIDAR_PIN, ESTADO_MENU_PRINCIPAL, ESTADO_CONSULTAR_SALDO, ESTADO_RETIRAR_DINERO, ESTADO_FINALIZAR, ESTADO_ERROR, ESTADO_MAX } estado_cajero_t; typedef struct { estado_cajero_t estado_actual; int pin_correcto; int intentos_pin; double saldo_cuenta; bool sesion_activa; } contexto_cajero_t; // Prototipos de funciones de estado void estado_inicio(contexto_cajero_t *ctx); void estado_validar_pin(contexto_cajero_t *ctx); void estado_menu_principal(contexto_cajero_t *ctx); void estado_consultar_saldo(contexto_cajero_t *ctx); void estado_retirar_dinero(contexto_cajero_t *ctx); void estado_finalizar(contexto_cajero_t *ctx); void estado_error(contexto_cajero_t *ctx); // Tabla de funciones para simular switch typedef void (*funcion_estado_t)(contexto_cajero_t *); static funcion_estado_t tabla_estados[ESTADO_MAX] = { [ESTADO_INICIO] = estado_inicio, [ESTADO_VALIDAR_PIN] = estado_validar_pin, [ESTADO_MENU_PRINCIPAL] = estado_menu_principal, [ESTADO_CONSULTAR_SALDO] = estado_consultar_saldo, [ESTADO_RETIRAR_DINERO] = estado_retirar_dinero, [ESTADO_FINALIZAR] = estado_finalizar, [ESTADO_ERROR] = estado_error }; void inicializar_cajero(contexto_cajero_t *ctx) { ctx->estado_actual = ESTADO_INICIO; ctx->pin_correcto = 1234; ctx->intentos_pin = 0; ctx->saldo_cuenta = 1500.00; ctx->sesion_activa = false; } void ejecutar_cajero(contexto_cajero_t *ctx) { // Simulación de do...while para la máquina de estados while (1) { if (ctx->estado_actual >= ESTADO_MAX) { printf("Estado inválido: %d\n", ctx->estado_actual); ctx->estado_actual = ESTADO_ERROR; } // Ejecutar función del estado actual (simulando switch con tabla) if (tabla_estados[ctx->estado_actual]) { tabla_estados[ctx->estado_actual](ctx); } // Condición de salida del bucle if (ctx->estado_actual == ESTADO_FINALIZAR) { break; } } } void estado_inicio(contexto_cajero_t *ctx) { printf("\n" "=" * 40 "\n"); printf("BIENVENIDO AL CAJERO AUTOMÁTICO\n"); printf("=" * 40 "\n"); printf("Por favor, inserte su tarjeta...\n"); printf("Presiona Enter para continuar: "); while (getchar() != '\n'); // Esperar Enter ctx->estado_actual = ESTADO_VALIDAR_PIN; } void estado_validar_pin(contexto_cajero_t *ctx) { int pin_ingresado; printf("\nVALIDACIÓN DE PIN\n"); printf("Ingrese su PIN (4 dígitos): "); if (scanf("%d", &pin_ingresado) != 1) { printf("PIN inválido. Use solo números.\n"); while (getchar() != '\n'); // Limpiar buffer ctx->intentos_pin++; } else if (pin_ingresado == ctx->pin_correcto) { printf("PIN correcto. Acceso autorizado.\n"); ctx->sesion_activa = true; ctx->intentos_pin = 0; ctx->estado_actual = ESTADO_MENU_PRINCIPAL; return; } else { printf("PIN incorrecto.\n"); ctx->intentos_pin++; } if (ctx->intentos_pin >= 3) { printf("Demasiados intentos fallidos. Tarjeta bloqueada.\n"); ctx->estado_actual = ESTADO_ERROR; } else { printf("Intentos restantes: %d\n", 3 - ctx->intentos_pin); } } void estado_menu_principal(contexto_cajero_t *ctx) { int opcion; printf("\nMENÚ PRINCIPAL\n"); printf("1. Consultar saldo\n"); printf("2. Retirar dinero\n"); printf("3. Finalizar sesión\n"); printf("Seleccione opción: "); if (scanf("%d", &opcion) != 1) { printf("Opción inválida.\n"); while (getchar() != '\n'); // Limpiar buffer return; } // Simulación de switch con if-else encadenados if (opcion == 1) { ctx->estado_actual = ESTADO_CONSULTAR_SALDO; } else if (opcion == 2) { ctx->estado_actual = ESTADO_RETIRAR_DINERO; } else if (opcion == 3) { ctx->estado_actual = ESTADO_FINALIZAR; } else { printf("Opción inválida: %d\n", opcion); } } void estado_consultar_saldo(contexto_cajero_t *ctx) { printf("\nCONSULTA DE SALDO\n"); printf("Su saldo actual es: $%.2f\n", ctx->saldo_cuenta); printf("\nPresiona Enter para volver al menú principal: "); while (getchar() != '\n'); // Limpiar buffer anterior while (getchar() != '\n'); // Esperar Enter ctx->estado_actual = ESTADO_MENU_PRINCIPAL; } void estado_retirar_dinero(contexto_cajero_t *ctx) { double monto; printf("\nRETIRO DE DINERO\n"); printf("Saldo disponible: $%.2f\n", ctx->saldo_cuenta); printf("Ingrese monto a retirar: $"); if (scanf("%lf", &monto) != 1) { printf("Monto inválido.\n"); while (getchar() != '\n'); // Limpiar buffer ctx->estado_actual = ESTADO_MENU_PRINCIPAL; return; } if (monto <= 0) { printf("El monto debe ser mayor a cero.\n"); } else if (monto > ctx->saldo_cuenta) { printf("Saldo insuficiente.\n"); } else { ctx->saldo_cuenta -= monto; printf("Retiro exitoso de $%.2f\n", monto); printf("Nuevo saldo: $%.2f\n", ctx->saldo_cuenta); printf("Por favor, retire su dinero de la bandeja.\n"); } printf("\nPresiona Enter para volver al menú principal: "); while (getchar() != '\n'); // Limpiar buffer anterior while (getchar() != '\n'); // Esperar Enter ctx->estado_actual = ESTADO_MENU_PRINCIPAL; } void estado_finalizar(contexto_cajero_t *ctx) { printf("\nFINALIZANDO SESIÓN\n"); printf("Gracias por usar nuestros servicios.\n"); printf("Por favor, retire su tarjeta.\n"); printf("¡Que tenga un buen día!\n"); ctx->sesion_activa = false; } void estado_error(contexto_cajero_t *ctx) { printf("\nERROR DEL SISTEMA\n"); printf("Se ha producido un error. La sesión será terminada.\n"); printf("Si el problema persiste, contacte al servicio técnico.\n"); ctx->estado_actual = ESTADO_FINALIZAR; } int main() { contexto_cajero_t cajero; inicializar_cajero(&cajero); ejecutar_cajero(&cajero); return 0; }
Solution to Exercise simulacion-3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <ctype.h> typedef struct { char nombre[100]; int edad; char email[150]; char telefono[20]; } datos_usuario_t; // Función auxiliar para limpiar buffer de entrada void limpiar_buffer() { int c; while ((c = getchar()) != '\n' && c != EOF); } // Validación 1: Nombre (usando while infinito con break) bool validar_nombre(char *nombre, size_t max_len) { printf("\nINGRESO DE NOMBRE\n"); while (1) { // Simulación de do...while printf("Ingrese su nombre completo (solo letras y espacios): "); if (!fgets(nombre, max_len, stdin)) { printf("Error leyendo entrada. Intente nuevamente.\n"); continue; } // Remover salto de línea nombre[strcspn(nombre, "\n")] = 0; // Validar que no esté vacío if (strlen(nombre) == 0) { printf("El nombre no puede estar vacío.\n"); continue; } // Validar que solo contenga letras y espacios bool valido = true; for (size_t i = 0; i < strlen(nombre); i++) { if (!isalpha(nombre[i]) && nombre[i] != ' ') { valido = false; break; } } if (!valido) { printf("El nombre solo debe contener letras y espacios.\n"); continue; } // Validar longitud mínima if (strlen(nombre) < 2) { printf("El nombre debe tener al menos 2 caracteres.\n"); continue; } printf("Nombre válido: %s\n", nombre); return true; } } // Validación 2: Edad (usando variable de control booleana) bool validar_edad(int *edad) { bool entrada_valida = false; char buffer[50]; printf("\nINGRESO DE EDAD\n"); while (!entrada_valida) { // Simulación con variable de control printf("Ingrese su edad (18-99 años): "); if (!fgets(buffer, sizeof(buffer), stdin)) { printf("Error leyendo entrada. Intente nuevamente.\n"); continue; } // Intentar convertir a entero char *endptr; long edad_temp = strtol(buffer, &endptr, 10); // Validar que la conversión fue exitosa if (endptr == buffer || *endptr != '\n') { printf("Por favor ingrese un número válido.\n"); continue; } // Validar rango if (edad_temp < 18 || edad_temp > 99) { printf("La edad debe estar entre 18 y 99 años.\n"); continue; } *edad = (int)edad_temp; entrada_valida = true; // Salir del bucle printf("Edad válida: %d años\n", *edad); } return true; } // Validación 3: Email (usando función con return temprano) bool obtener_email_valido(char *email, size_t max_len) { printf("\nINGRESO DE EMAIL\n"); while (1) { printf("Ingrese su email: "); if (!fgets(email, max_len, stdin)) { printf("Error leyendo entrada. Intente nuevamente.\n"); continue; } // Remover salto de línea email[strcspn(email, "\n")] = 0; // Validar longitud mínima if (strlen(email) < 5) { printf("El email es demasiado corto.\n"); continue; } // Buscar @ (validación básica) char *at_pos = strchr(email, '@'); if (!at_pos) { printf("El email debe contener '@'.\n"); continue; } // Validar que @ no esté al inicio o al final if (at_pos == email || at_pos == email + strlen(email) - 1) { printf("El '@' no puede estar al inicio o al final.\n"); continue; } // Buscar punto después del @ char *dot_pos = strchr(at_pos, '.'); if (!dot_pos) { printf("El email debe contener un punto después del '@'.\n"); continue; } // Validar que haya caracteres después del último punto if (dot_pos == email + strlen(email) - 1) { printf("Debe haber caracteres después del último punto.\n"); continue; } printf("Email válido: %s\n", email); return true; // Return temprano para salir } } // Validación 4: Teléfono (usando contador de intentos) bool validar_telefono(char *telefono, size_t max_len) { int intentos = 0; const int max_intentos = 3; printf("\n📱 INGRESO DE TELÉFONO\n"); while (intentos < max_intentos) { // Simulación con límite de intentos printf("Ingrese su teléfono (solo números, 8-15 dígitos): "); if (!fgets(telefono, max_len, stdin)) { printf("Error leyendo entrada.\n"); intentos++; continue; } // Remover salto de línea telefono[strcspn(telefono, "\n")] = 0; // Validar que solo contenga dígitos bool solo_numeros = true; size_t len = strlen(telefono); if (len == 0) { printf("El teléfono no puede estar vacío.\n"); intentos++; continue; } for (size_t i = 0; i < len; i++) { if (!isdigit(telefono[i])) { solo_numeros = false; break; } } if (!solo_numeros) { printf("El teléfono solo debe contener números.\n"); intentos++; continue; } // Validar longitud if (len < 8 || len > 15) { printf("El teléfono debe tener entre 8 y 15 dígitos.\n"); intentos++; continue; } printf("Teléfono válido: %s\n", telefono); return true; } printf("Demasiados intentos fallidos para el teléfono.\n"); return false; } void mostrar_resumen(const datos_usuario_t *datos) { printf("\n" "=" * 50 "\n"); printf("RESUMEN DE DATOS INGRESADOS\n"); printf("=" * 50 "\n"); printf("Nombre: %s\n", datos->nombre); printf("Edad: %d años\n", datos->edad); printf("Email: %s\n", datos->email); printf("Teléfono: %s\n", datos->telefono); printf("=" * 50 "\n"); printf("Todos los datos han sido validados correctamente.\n"); } bool confirmar_datos(const datos_usuario_t *datos) { char respuesta[10]; mostrar_resumen(datos); while (1) { printf("\n¿Los datos son correctos? (s/n): "); if (!fgets(respuesta, sizeof(respuesta), stdin)) { printf("Error leyendo respuesta.\n"); continue; } respuesta[strcspn(respuesta, "\n")] = 0; if (strlen(respuesta) != 1) { printf("Por favor ingrese solo 's' o 'n'.\n"); continue; } char opcion = tolower(respuesta[0]); if (opcion == 's') { return true; } else if (opcion == 'n') { return false; } else { printf("Respuesta inválida. Use 's' para sí o 'n' para no.\n"); } } } int main() { datos_usuario_t datos; bool datos_completos = false; printf("SISTEMA DE REGISTRO DE USUARIO\n"); printf("Por favor, complete los siguientes datos:\n"); // Bucle principal usando simulación de do...while while (!datos_completos) { // Limpiar estructura memset(&datos, 0, sizeof(datos)); // Validar cada campo usando diferentes técnicas de simulación if (!validar_nombre(datos.nombre, sizeof(datos.nombre))) { printf("Error validando nombre. Reiniciando proceso.\n"); continue; } if (!validar_edad(&datos.edad)) { printf("Error validando edad. Reiniciando proceso.\n"); continue; } if (!obtener_email_valido(datos.email, sizeof(datos.email))) { printf("Error validando email. Reiniciando proceso.\n"); continue; } if (!validar_telefono(datos.telefono, sizeof(datos.telefono))) { printf("Error validando teléfono. Reiniciando proceso.\n"); continue; } // Confirmar datos con el usuario if (confirmar_datos(&datos)) { datos_completos = true; printf("\nRegistro completado exitosamente!\n"); } else { printf("\nReiniciando el proceso de registro...\n"); } } return 0; }