Ejercicios: Refactorización de Código Ofuscado
Deconstrucción, análisis y transformación de código críptico a C idiomático y limpio
Acerca de¶
En el desarrollo profesional de software, la mayor parte del tiempo se destina a leer, entender y mantener código existente. Con frecuencia te encontrarás con código mal estructurado, con nombres crípticos de una sola letra, números mágicos sin explicación, anidaciones excesivas o dependencias ocultas de variables globales.
El objetivo de este cuadernillo es desarrollar tu agudeza analítica mediante la deconstrucción y refactorización de fragmentos de código intencionalmente ofuscados.
Metodología de Trabajo para Cada Ejercicio¶
Para resolver cada ejercicio debés:
Analizar el código ofuscado: Determinar qué hace el algoritmo, cuáles son sus datos de entrada y salida, y qué invariantes o precondiciones asume.
Identificar “olores de código” (code smells) y violaciones de estilo: Nombres crípticos, operador ternario no permitido, falta de llaves Allman, variables reutilizadas, números mágicos, efectos secundarios, etc.
Refactorizar a C limpio e idiomático: Reescribir la solución aplicando:
Formato Allman (Regla 0x000Bh).
Nombres autoexplicativos en
snake_case(Regla 0x0007h).Eliminación estricta de variables globales (Regla 0x2004h).
Cláusulas de guarda para evitar anidaciones (Regla 0x2001h).
Constantes simbólicas para números mágicos (Regla 0x0004h).
Desacople total de
printf/scanf(Regla 0x2002h).
Diseñar una suite de pruebas con
assert(): Validar que la versión refactorizada preserve exactamente el comportamiento esperado ante casos normales y bordes.
1. Expresiones Aritméticas, Asignaciones y Nombres Crípticos¶
Ejercicio 9.1 - El Máximo Comprimido ⭐☆☆☆☆¶
Solution to Exercise 1
Diagnóstico:
Violación de la Regla 0x1007h: uso del operador ternario
?:.Violación de la Regla 0x000Bh: llaves en la misma línea (no es Allman).
Violación de la Regla 0x0007h: identificadores de una letra
f, a, b.
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20#include <assert.h> int obtener_mayor(int primer_numero, int segundo_numero) { if (primer_numero > segundo_numero) { return primer_numero; } else { return segundo_numero; } } void test_obtener_mayor(void) { assert(obtener_mayor(10, 5) == 10); assert(obtener_mayor(-3, -8) == -3); assert(obtener_mayor(7, 7) == 7); }
Ejercicio 9.2 - Geometría sin Nombres ⭐☆☆☆☆¶
Solution to Exercise 2
Diagnóstico: Calcula el área de un triángulo con base a y altura b. No
valida dimensiones no positivas.
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16#include <assert.h> double calcular_area_triangulo(double base, double altura) { if (base <= 0.0 || altura <= 0.0) { return -1.0; } return (base * altura) / 2.0; } void test_area_triangulo(void) { assert(calcular_area_triangulo(10.0, 5.0) == 25.0); assert(calcular_area_triangulo(-2.0, 5.0) == -1.0); }
Ejercicio 9.3 - Suma de Cuadrados en Una Línea ⭐⭐☆☆☆¶
Solution to Exercise 3
Diagnóstico: Calcula . Carece de espacios, llaves Allman y validación para .
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22#include <assert.h> int calcular_suma_cuadrados(int limite) { if (limite < 0) { return -1; } int acumulador = 0; for (int i = 1; i <= limite; i++) { acumulador += (i * i); } return acumulador; } void test_suma_cuadrados(void) { assert(calcular_suma_cuadrados(0) == 0); assert(calcular_suma_cuadrados(3) == 14); // 1 + 4 + 9 = 14 assert(calcular_suma_cuadrados(-5) == -1); }
Ejercicio 9.4 - Conversión Térmica con División Entera Peligrosa ⭐⭐☆☆☆¶
Solution to Exercise 4
Diagnóstico: Nombres opacos, falta de constantes simbólicas y sin verificación del límite físico del cero absoluto.
Código Refactorizado:
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#include <assert.h> #include <math.h> #include <stdbool.h> #define CERO_ABSOLUTO_CELSIUS -273.15 #define FACTOR_ESCALA (9.0 / 5.0) #define DESPLAZAMIENTO_FAHRENHEIT 32.0 double convertir_celsius_a_fahrenheit(double temperatura_celsius, bool *es_valida) { if (temperatura_celsius < CERO_ABSOLUTO_CELSIUS || es_valida == NULL) { if (es_valida != NULL) { *es_valida = false; } return 0.0; } *es_valida = true; return (temperatura_celsius * FACTOR_ESCALA) + DESPLAZAMIENTO_FAHRENHEIT; } void test_conversion_celsius(void) { bool ok = false; double f = convertir_celsius_a_fahrenheit(0.0, &ok); assert(ok && fabs(f - 32.0) < 1e-6); f = convertir_celsius_a_fahrenheit(100.0, &ok); assert(ok && fabs(f - 212.0) < 1e-6); }
Ejercicio 9.5 - Suma de Dígitos Ofuscada ⭐⭐☆☆☆¶
Solution to Exercise 5
Diagnóstico: Suma los dígitos de un número. Para -123, n % 10 produce
restos negativos en C99+, dando una suma errónea (-1 + -2 + -3 = -6).
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21#include <assert.h> #include <stdlib.h> int sumar_digitos(int numero) { int n = abs(numero); int suma = 0; while (n > 0) { suma += (n % 10); n /= 10; } return suma; } void test_sumar_digitos(void) { assert(sumar_digitos(1234) == 10); assert(sumar_digitos(-505) == 10); assert(sumar_digitos(0) == 0); }
Ejercicio 9.6 - Año Bisiesto Críptico ⭐☆☆☆☆¶
Solution to Exercise 6
Código Refactorizado:
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 <assert.h> #include <stdbool.h> bool es_anio_bisiesto(int anio) { if (anio <= 0) { return false; } bool es_div_4 = (anio % 4 == 0); bool es_div_100 = (anio % 100 == 0); bool es_div_400 = (anio % 400 == 0); return (es_div_4 && !es_div_100) || es_div_400; } void test_es_anio_bisiesto(void) { assert(es_anio_bisiesto(2024) == true); assert(es_anio_bisiesto(2023) == false); assert(es_anio_bisiesto(1900) == false); assert(es_anio_bisiesto(2000) == true); assert(es_anio_bisiesto(-4) == false); }
Ejercicio 9.7 - Sumatoria de Gauss con Nombres Inadecuados ⭐☆☆☆☆¶
Solution to Exercise 7
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19#include <assert.h> long long calcular_sumatoria_gauss(int limite_superior) { if (limite_superior < 0) { return -1; } long long n = limite_superior; return (n * (n + 1)) / 2; } void test_sumatoria_gauss(void) { assert(calcular_sumatoria_gauss(0) == 0); assert(calcular_sumatoria_gauss(10) == 55); assert(calcular_sumatoria_gauss(100) == 5050); assert(calcular_sumatoria_gauss(-5) == -1); }
Ejercicio 9.8 - Cilindro con Número Mágico ⭐⭐☆☆☆¶
Solution to Exercise 8
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20#include <assert.h> #include <math.h> #define CONSTANTE_PI 3.14159265358979323846 double calcular_volumen_cilindro(double radio, double altura) { if (radio <= 0.0 || altura <= 0.0) { return -1.0; } return CONSTANTE_PI * radio * radio * altura; } void test_volumen_cilindro(void) { double vol = calcular_volumen_cilindro(1.0, 10.0); assert(fabs(vol - (CONSTANTE_PI * 10.0)) < 1e-6); assert(calcular_volumen_cilindro(-1.0, 5.0) == -1.0); }
2. Control de Flujo Espagueti y Anidaciones Profundas¶
Ejercicio 9.9 - El Monstruo de la Flecha (Arrow Code) ⭐⭐⭐☆☆¶
Solution to Exercise 9
Diagnóstico: Anidación excesiva de 5 niveles que dificulta el seguimiento de los casos de error.
Código Refactorizado:
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#include <assert.h> #define ESTADO_NOTA_INVALIDA -1 #define ESTADO_LIBRE 0 #define ESTADO_REPROBADO 1 #define ESTADO_APROBADO 2 #define ESTADO_SOBRESALIENTE 3 int clasificar_desempenio(int nota, int asistencia_porcentaje) { if (nota < 0 || nota > 100) { return ESTADO_NOTA_INVALIDA; } if (asistencia_porcentaje < 75) { return ESTADO_LIBRE; } if (nota >= 90) { return ESTADO_SOBRESALIENTE; } if (nota >= 60) { return ESTADO_APROBADO; } return ESTADO_REPROBADO; } void test_clasificar_desempenio(void) { assert(clasificar_desempenio(95, 80) == ESTADO_SOBRESALIENTE); assert(clasificar_desempenio(70, 80) == ESTADO_APROBADO); assert(clasificar_desempenio(50, 80) == ESTADO_REPROBADO); assert(clasificar_desempenio(80, 50) == ESTADO_LIBRE); assert(clasificar_desempenio(105, 80) == ESTADO_NOTA_INVALIDA); }
Ejercicio 9.10 - Menú con Ifs Caóticos ⭐⭐☆☆☆¶
Solution to Exercise 10
Código Refactorizado:
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#include <assert.h> #include <stdbool.h> typedef enum { OP_SUMAR = 1, OP_RESTAR = 2, OP_MULTIPLICAR = 3, OP_DIVIDIR = 4 } tipo_operacion_t; double calcular_operacion(tipo_operacion_t operacion, double a, double b, bool *exito) { if (exito == NULL) { return 0.0; } *exito = true; switch (operacion) { case OP_SUMAR: return a + b; case OP_RESTAR: return a - b; case OP_MULTIPLICAR: return a * b; case OP_DIVIDIR: if (b == 0.0) { *exito = false; return 0.0; } return a / b; default: *exito = false; return 0.0; } } void test_operaciones(void) { bool ok = false; assert(calcular_operacion(OP_SUMAR, 3.0, 4.0, &ok) == 7.0 && ok); assert(calcular_operacion(OP_DIVIDIR, 10.0, 2.0, &ok) == 5.0 && ok); calcular_operacion(OP_DIVIDIR, 10.0, 0.0, &ok); assert(!ok); }
Ejercicio 9.11 - Lazo Espagueti con Banderas Confusas ⭐⭐⭐☆☆¶
Ejercicio 9.12 - Tarifa de Taxi con Variables Reutilizadas ⭐⭐☆☆☆¶
Solution to Exercise 12
Código Refactorizado:
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 <assert.h> #include <stdbool.h> #define BAJADA_DE_BANDERA 150.0 #define COSTO_POR_KILOMETRO 80.0 #define FACTOR_RECARGO_NOCTURNO 1.20 double calcular_tarifa_taxi(double kilometros, bool es_tarifa_nocturna) { if (kilometros < 0.0) { return -1.0; } double costo_distancia = kilometros * COSTO_POR_KILOMETRO; double subtotal = BAJADA_DE_BANDERA + costo_distancia; if (es_tarifa_nocturna) { return subtotal * FACTOR_RECARGO_NOCTURNO; } return subtotal; } void test_tarifa_taxi(void) { assert(calcular_tarifa_taxi(10.0, false) == 950.0); assert(calcular_tarifa_taxi(10.0, true) == 1140.0); assert(calcular_tarifa_taxi(-5.0, false) == -1.0); }
Ejercicio 9.13 - Expresión Booleana Monolítica de Validación de Fecha ⭐⭐⭐☆☆¶
Ejercicio 9.14 - Descuento Escalonado con Condiciones Solapadas ⭐⭐☆☆☆¶
Solution to Exercise 14
Código Refactorizado:
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#include <assert.h> #define UMBRAL_NIVEL_1 1000.0 #define UMBRAL_NIVEL_2 5000.0 #define UMBRAL_NIVEL_3 10000.0 #define FACTOR_DESCUENTO_1 0.95 #define FACTOR_DESCUENTO_2 0.90 #define FACTOR_DESCUENTO_3 0.80 double aplicar_descuento_escalonado(double monto_compra) { if (monto_compra <= 0.0) { return 0.0; } if (monto_compra >= UMBRAL_NIVEL_3) { return monto_compra * FACTOR_DESCUENTO_3; } if (monto_compra >= UMBRAL_NIVEL_2) { return monto_compra * FACTOR_DESCUENTO_2; } if (monto_compra >= UMBRAL_NIVEL_1) { return monto_compra * FACTOR_DESCUENTO_1; } return monto_compra; } void test_descuentos(void) { assert(aplicar_descuento_escalonado(500.0) == 500.0); assert(aplicar_descuento_escalonado(2000.0) == 1900.0); assert(aplicar_descuento_escalonado(6000.0) == 5400.0); assert(aplicar_descuento_escalonado(12000.0) == 9600.0); }
Ejercicio 9.15 - Función Contaminada con Entrada/Salida ⭐⭐☆☆☆¶
Solution to Exercise 15
Código Refactorizado:
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#include <assert.h> typedef enum { CLIMA_FRIO, CLIMA_TEMPLADO, CLIMA_CALUROSO } tipo_clima_t; tipo_clima_t clasificar_temperatura(double temperatura_celsius) { if (temperatura_celsius < 10.0) { return CLIMA_FRIO; } if (temperatura_celsius <= 25.0) { return CLIMA_TEMPLADO; } return CLIMA_CALUROSO; } void test_clasificar_clima(void) { assert(clasificar_temperatura(5.0) == CLIMA_FRIO); assert(clasificar_temperatura(18.0) == CLIMA_TEMPLADO); assert(clasificar_temperatura(30.0) == CLIMA_CALUROSO); }
Ejercicio 9.16 - Búsqueda Lineal con Variables Globales ⭐⭐☆☆☆¶
Solution to Exercise 16
Código Refactorizado:
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 <assert.h> int buscar_primera_aparicion(const int arreglo[], int cantidad_elementos, int objetivo) { if (arreglo == NULL || cantidad_elementos <= 0) { return -1; } for (int i = 0; i < cantidad_elementos; i++) { if (arreglo[i] == objetivo) { return i; } } return -1; } void test_buscar_aparicion(void) { int datos[] = {10, 20, 30, 40}; assert(buscar_primera_aparicion(datos, 4, 30) == 2); assert(buscar_primera_aparicion(datos, 4, 99) == -1); }
3. Algoritmos Comprimidos y Lazos Crípticos¶
Ejercicio 9.17 - Euclides con Operador Coma y Asignaciones Crípticas ⭐⭐⭐☆☆¶
Ejercicio 9.18 - Inversor de Números con Variables Reutilizadas ⭐⭐☆☆☆¶
Solution to Exercise 18
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23#include <assert.h> #include <stdlib.h> int invertir_numero_entero(int numero) { int signo = (numero < 0) ? -1 : 1; int n = abs(numero); int invertido = 0; while (n > 0) { invertido = (invertido * 10) + (n % 10); n /= 10; } return invertido * signo; } void test_invertir_numero(void) { assert(invertir_numero_entero(1234) == 4321); assert(invertir_numero_entero(-580) == -85); assert(invertir_numero_entero(0) == 0); }
Ejercicio 9.19 - Fibonacci Iterativo Micro-comprimido ⭐⭐☆☆☆¶
Solution to Exercise 19
Código Refactorizado:
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#include <assert.h> long long calcular_fibonacci(int posicion) { if (posicion < 0) { return -1; } if (posicion == 0) { return 0; } if (posicion == 1) { return 1; } long long anterior = 0; long long actual = 1; for (int i = 2; i <= posicion; i++) { long long siguiente = anterior + actual; anterior = actual; actual = siguiente; } return actual; } void test_fibonacci(void) { assert(calcular_fibonacci(0) == 0); assert(calcular_fibonacci(1) == 1); assert(calcular_fibonacci(6) == 8); assert(calcular_fibonacci(10) == 55); }
Ejercicio 9.20 - Conteo de Números Armstrong Desordenado ⭐⭐⭐☆☆¶
Ejercicio 9.21 - Factorial con Detección de Desbordamiento Oculto ⭐⭐☆☆☆¶
Solution to Exercise 21
Código Refactorizado:
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 <assert.h> #define MAX_N_FACTORIAL_64BITS 20 long long calcular_factorial(int n) { if (n < 0 || n > MAX_N_FACTORIAL_64BITS) { return -1; } long long resultado = 1; for (int factor = 2; factor <= n; factor++) { resultado *= factor; } return resultado; } void test_factorial(void) { assert(calcular_factorial(0) == 1); assert(calcular_factorial(5) == 120); assert(calcular_factorial(21) == -1); // Error de desbordamiento }
Ejercicio 9.22 - Números Perfectos con Lazo Ineficiente ⭐⭐⭐☆☆¶
Ejercicio 9.23 - Cálculo de Potencia Rápida Oculto ⭐⭐⭐☆☆¶
Ejercicio 9.24 - Desglose de Dinero con Lazos Confusos ⭐⭐☆☆☆¶
Solution to Exercise 24
Código Refactorizado:
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#include <assert.h> #include <stdbool.h> bool desglosar_cambio(int monto, int *monedas_100, int *monedas_50, int *monedas_10) { if (monto < 0 || monedas_100 == NULL || monedas_50 == NULL || monedas_10 == NULL) { return false; } *monedas_100 = monto / 100; monto %= 100; *monedas_50 = monto / 50; monto %= 50; *monedas_10 = monto / 10; return true; } void test_desglose_cambio(void) { int m100 = 0, m50 = 0, m10 = 0; bool ok = desglosar_cambio(370, &m100, &m50, &m10); assert(ok && m100 == 3 && m50 == 1 && m10 == 2); }
4. Descomposición Modular y Funciones Reutilizables¶
Ejercicio 9.25 - La Función Monstruo de Facturación ⭐⭐⭐☆☆¶
Ejercicio 9.26 - Herón y Clasificación de Triángulos Todo-en-Uno ⭐⭐⭐☆☆¶
Ejercicio 9.27 - Duplicación de Código de Tiempo (Violación DRY) ⭐⭐☆☆☆¶
Solution to Exercise 27
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22#include <assert.h> #include <stdbool.h> bool descomponer_segundos(int total_segundos, int *horas, int *minutos, int *segundos) { if (total_segundos < 0 || horas == NULL || minutos == NULL || segundos == NULL) { return false; } *horas = total_segundos / 3600; int resto = total_segundos % 3600; *minutos = resto / 60; *segundos = resto % 60; return true; } void test_descomponer_segundos(void) { int h = 0, m = 0, s = 0; assert(descomponer_segundos(3665, &h, &m, &s)); assert(h == 1 && m == 1 && s == 5); }
Ejercicio 9.28 - Máquina de Estados con Estados Mágicos ⭐⭐⭐☆☆¶
Ejercicio 9.29 - Validación de Entrada con Lazos Desacoplados ⭐⭐☆☆☆¶
Solution to Exercise 29
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16#include <assert.h> #include <stdbool.h> bool esta_en_rango(int valor, int limite_inferior, int limite_superior) { return (valor >= limite_inferior && valor <= limite_superior); } void test_esta_en_rango(void) { assert(esta_en_rango(5, 1, 10) == true); assert(esta_en_rango(1, 1, 10) == true); assert(esta_en_rango(10, 1, 10) == true); assert(esta_en_rango(0, 1, 10) == false); assert(esta_en_rango(11, 1, 10) == false); }
Ejercicio 9.30 - Tabla de Multiplicar Formateada y Modularizada ⭐⭐☆☆☆¶
Solution to Exercise 30
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23#include <assert.h> #include <stdio.h> int calcular_producto(int multiplicando, int multiplicador) { return multiplicando * multiplicador; } void imprimir_tabla_multiplicar(int numero) { for (int factor = 1; factor <= 10; factor++) { int resultado = calcular_producto(numero, factor); printf("%2d x %2d = %4d\n", numero, factor, resultado); } } void test_calcular_producto(void) { assert(calcular_producto(7, 8) == 56); assert(calcular_producto(0, 5) == 0); assert(calcular_producto(-3, 4) == -12); }
Ejercicio 9.31 - Algoritmo de Luhn Comprimido ⭐⭐⭐⭐⭐¶
Ejercicio 9.32 - Congruencia de Zeller Astronómica ⭐⭐⭐⭐⭐¶
5. Lazos Anidados, Matrices y Patrones Numéricos¶
Ejercicio 9.33 - Rectángulo de Asteriscos Comprimido ⭐☆☆☆☆¶
Solution to Exercise 33
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22#include <stdio.h> void imprimir_fila_caracter(char caracter, int longitud) { for (int columna = 0; columna < longitud; columna++) { putchar(caracter); } putchar('\n'); } void imprimir_rectangulo(int ancho, int alto) { if (ancho <= 0 || alto <= 0) { return; } for (int fila = 0; fila < alto; fila++) { imprimir_fila_caracter('*', ancho); } }
Ejercicio 9.34 - Matriz Identidad con Ternario Críptico ⭐⭐☆☆☆¶
Solution to Exercise 34
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22#include <assert.h> int obtener_valor_identidad(int fila, int columna) { if (fila < 0 || columna < 0) { return -1; } if (fila == columna) { return 1; } return 0; } void test_valor_identidad(void) { assert(obtener_valor_identidad(0, 0) == 1); assert(obtener_valor_identidad(2, 2) == 1); assert(obtener_valor_identidad(0, 3) == 0); assert(obtener_valor_identidad(-1, 0) == -1); }
Ejercicio 9.35 - Conteo de Vocales con Cascada Repetitiva ⭐⭐☆☆☆¶
Solution to Exercise 35
Código Refactorizado:
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#include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stddef.h> bool es_vocal(char caracter) { char c = (char)tolower((unsigned char)caracter); switch (c) { case 'a': case 'e': case 'i': case 'o': case 'u': return true; default: return false; } } int contar_vocales_secuencia(const char caracteres[], int longitud) { if (caracteres == NULL || longitud <= 0) { return 0; } int total_vocales = 0; for (int i = 0; i < longitud; i++) { if (es_vocal(caracteres[i])) { total_vocales++; } } return total_vocales; } void test_conteo_vocales(void) { char mensaje[] = {'H', 'o', 'l', 'a', 'M', 'u', 'n', 'd', 'o'}; assert(contar_vocales_secuencia(mensaje, 9) == 4); assert(contar_vocales_secuencia(NULL, 0) == 0); }
Ejercicio 9.36 - Coeficiente Binomial Redundante ⭐⭐⭐☆☆¶
Ejercicio 9.37 - Primos Gemelos con Variables Globales ⭐⭐☆☆☆¶
Solution to Exercise 37
Código Refactorizado:
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#include <assert.h> #include <stdbool.h> static bool es_primo_auxiliar(int n) { if (n <= 1) { return false; } if (n == 2) { return true; } if (n % 2 == 0) { return false; } for (int d = 3; d * d <= n; d += 2) { if (n % d == 0) { return false; } } return true; } bool son_primos_gemelos(int primer_numero, int segundo_numero) { if (segundo_numero != primer_numero + 2) { return false; } return es_primo_auxiliar(primer_numero) && es_primo_auxiliar(segundo_numero); } void test_primos_gemelos(void) { assert(son_primos_gemelos(3, 5) == true); assert(son_primos_gemelos(11, 13) == true); assert(son_primos_gemelos(7, 9) == false); assert(son_primos_gemelos(4, 6) == false); }
Ejercicio 9.38 - Criba de Divisores Estática ⭐⭐⭐☆☆¶
Ejercicio 9.39 - Suma de Diagonales en Arreglo 2D Plano ⭐⭐☆☆☆¶
Solution to Exercise 39
Código Refactorizado:
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#include <assert.h> #include <stddef.h> int calcular_suma_diagonales(const int matriz_plana[], int dimension) { if (matriz_plana == NULL || dimension <= 0) { return 0; } int suma = 0; for (int i = 0; i < dimension; i++) { int indice_diag_principal = (i * dimension) + i; int indice_diag_secundaria = (i * dimension) + (dimension - 1 - i); suma += matriz_plana[indice_diag_principal]; suma += matriz_plana[indice_diag_secundaria]; } // Si la dimensión es impar, el elemento central fue sumado dos veces if (dimension % 2 != 0) { int centro = dimension / 2; int indice_centro = (centro * dimension) + centro; suma -= matriz_plana[indice_centro]; } return suma; } void test_suma_diagonales(void) { int m3[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; assert(calcular_suma_diagonales(m3, 3) == 25); }
Ejercicio 9.40 - Conjetura de Collatz Caótica ⭐⭐⭐☆☆¶
Ejercicio 9.41 - Transposición de Matriz ⭐⭐☆☆☆¶
Solution to Exercise 41
Código Refactorizado:
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#include <assert.h> #include <stddef.h> #define MAT_FILAS 3 #define MAT_COLUMNAS 3 void transponer_matriz_3x3(const int origen[9], int destino[9]) { if (origen == NULL || destino == NULL) { return; } for (int fila = 0; fila < MAT_FILAS; fila++) { for (int col = 0; col < MAT_COLUMNAS; col++) { int idx_origen = (fila * MAT_COLUMNAS) + col; int idx_destino = (col * MAT_FILAS) + fila; destino[idx_destino] = origen[idx_origen]; } } } void test_transposicion_3x3(void) { int orig[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int dest[9] = {0}; transponer_matriz_3x3(orig, dest); assert(dest[0] == 1 && dest[1] == 4 && dest[2] == 7); assert(dest[3] == 2 && dest[4] == 5 && dest[5] == 8); assert(dest[6] == 3 && dest[7] == 6 && dest[8] == 9); }
Ejercicio 9.42 - Multiplicación de Matrices Cuadradas Comprimida ⭐⭐⭐⭐☆¶
6. Validación de Formatos, Parsing y Protocolos Simples¶
Ejercicio 9.43 - Validador de Hora Militar Comprimido ⭐☆☆☆☆¶
Solution to Exercise 43
Código Refactorizado:
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#include <assert.h> #include <stdbool.h> #define MIN_HORAS 0 #define MAX_HORAS 23 #define MIN_MINUTOS 0 #define MAX_MINUTOS 59 #define MIN_SEGUNDOS 0 #define MAX_SEGUNDOS 59 bool es_horario_valido(int horas, int minutos, int segundos) { if (horas < MIN_HORAS || horas > MAX_HORAS) { return false; } if (minutos < MIN_MINUTOS || minutos > MAX_MINUTOS) { return false; } if (segundos < MIN_SEGUNDOS || segundos > MAX_SEGUNDOS) { return false; } return true; } void test_es_horario_valido(void) { assert(es_horario_valido(14, 30, 0) == true); assert(es_horario_valido(23, 59, 59) == true); assert(es_horario_valido(24, 0, 0) == false); assert(es_horario_valido(12, -1, 0) == false); }
Ejercicio 9.44 - Conversor de Caracteres a Entero (atoi Casero) ⭐⭐☆☆☆¶
Solution to Exercise 44
Código Refactorizado:
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#include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stddef.h> int convertir_caracteres_a_entero(const char digitos[], int longitud, bool *exito) { if (digitos == NULL || longitud <= 0 || exito == NULL) { if (exito != NULL) { *exito = false; } return 0; } *exito = true; int signo = 1; int indice_inicio = 0; if (digitos[0] == '-') { signo = -1; indice_inicio = 1; if (longitud == 1) { *exito = false; return 0; } } int acumulador = 0; for (int i = indice_inicio; i < longitud; i++) { if (!isdigit((unsigned char)digitos[i])) { *exito = false; return 0; } acumulador = (acumulador * 10) + (digitos[i] - '0'); } return acumulador * signo; } void test_convertir_a_entero(void) { bool ok = false; char num1[] = {'1', '2', '3', '4'}; assert(convertir_caracteres_a_entero(num1, 4, &ok) == 1234 && ok); char num2[] = {'-', '5', '0'}; assert(convertir_caracteres_a_entero(num2, 3, &ok) == -50 && ok); char inv[] = {'1', '2', 'x', '4'}; convertir_caracteres_a_entero(inv, 4, &ok); assert(!ok); }
Ejercicio 9.45 - Validador de Octetos IPv4 Caótico ⭐⭐⭐☆☆¶
Ejercicio 9.46 - Verificador de CUIT/CUIL con Módulo 11 ⭐⭐☆☆☆¶
Solution to Exercise 46
Código Refactorizado:
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#include <assert.h> #include <stddef.h> #define CANTIDAD_DIGITOS_BASE_CUIT 10 int calcular_digito_verificador_cuit(const int primeros_10_digitos[10]) { if (primeros_10_digitos == NULL) { return -1; } static const int factores_ponderacion[10] = {5, 4, 3, 2, 7, 6, 5, 4, 3, 2}; int suma_ponderada = 0; for (int i = 0; i < CANTIDAD_DIGITOS_BASE_CUIT; i++) { if (primeros_10_digitos[i] < 0 || primeros_10_digitos[i] > 9) { return -1; } suma_ponderada += (primeros_10_digitos[i] * factores_ponderacion[i]); } int resto = suma_ponderada % 11; int digito_calculado = 11 - resto; if (digito_calculado == 11) { return 0; } if (digito_calculado == 10) { return 9; } return digito_calculado; } void test_cuit_verificador(void) { int digitos_test[10] = {2, 0, 1, 2, 3, 4, 5, 6, 7, 8}; int dv = calcular_digito_verificador_cuit(digitos_test); assert(dv >= 0 && dv <= 9); }
Ejercicio 9.47 - Compactador de Espacios Repetidos ⭐⭐⭐☆☆¶
Ejercicio 9.48 - Validador de Fortaleza de Contraseña ⭐⭐☆☆☆¶
Solution to Exercise 48
Código Refactorizado:
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#include <assert.h> #include <ctype.h> #include <stdbool.h> #include <stddef.h> #define LONGITUD_MINIMA_CLAVE 8 bool validar_fortaleza_clave(const char clave[], int longitud) { if (clave == NULL || longitud < LONGITUD_MINIMA_CLAVE) { return false; } bool tiene_mayuscula = false; bool tiene_digito = false; for (int i = 0; i < longitud; i++) { if (isupper((unsigned char)clave[i])) { tiene_mayuscula = true; } if (isdigit((unsigned char)clave[i])) { tiene_digito = true; } } return tiene_mayuscula && tiene_digito; } void test_fortaleza_clave(void) { char c1[] = {'P', 'a', 's', 's', 'w', 'o', 'r', 'd', '1'}; assert(validar_fortaleza_clave(c1, 9) == true); char c2[] = {'p', 'a', 's', 's', '1'}; assert(validar_fortaleza_clave(c2, 5) == false); char c3[] = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd', 's'}; assert(validar_fortaleza_clave(c3, 9) == false); }
Ejercicio 9.49 - Decodificador Run-Length (RLE) Numérico ⭐⭐⭐☆☆¶
Ejercicio 9.50 - Conversor de Números Romanos Básicos ⭐⭐☆☆☆¶
Solution to Exercise 50
Código Refactorizado:
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#include <assert.h> int obtener_valor_simbolo_romano(char simbolo) { switch (simbolo) { case 'I': return 1; case 'V': return 5; case 'X': return 10; case 'L': return 50; case 'C': return 100; case 'D': return 500; case 'M': return 1000; default: return -1; } } void test_simbolos_romanos(void) { assert(obtener_valor_simbolo_romano('X') == 10); assert(obtener_valor_simbolo_romano('M') == 1000); assert(obtener_valor_simbolo_romano('A') == -1); }