Ejercicios de Punteros Avanzados y Genericidad¶
Prerrequisitos y Entorno de Ejecución Requerido¶
Para compilar y resolver los módulos de esta guía se requiere:
Entorno de Compilación: GCC 9+ o Clang bajo estándar estricto ISO C11 (
-std=c11 -Wall -Wextra -Werror -pedantic).Indirección Múltiple y Callbacks: Punteros dobles (
T **), punteros a funciones (typedef R (*callback_t)(args)), genericidad basada en memoria opaca (void *), aritmética sobrechar *y cualificadorconst.Gestión Dinámica de Memoria: Asignación segura con
malloc/realloc, liberación simétrica confreey prevención rigurosa de fugas con sanitizers.
Objetivos Pedagógicos y Competencias (Taxonomía de Bloom)¶
Nivel Cognitivo: Nivel 4 (Analizar) a Nivel 5 (Evaluar).
Competencias Formativas: Diseño de funciones de orden superior (
map,filter,fold), tablas de despacho polimórficas y máquinas de estado finito orientadas a eventos.Rigor Contractual: Manejo defensivo ante punteros
NULL, contratos formales de precondición y verificación determinista conassert().
Acerca de¶
Estos ejercicios tienen como fin dominar la indirección múltiple mediante punteros dobles (**) y la abstracción procedimental mediante punteros a funciones (callback) para implementar polimorfismo y genericidad en C11.
Capítulos de Apunte Correspondientes¶
Indirección Múltiple: Punteros Dobles (**)¶
Ejercicio 4.02.01 - Asignación de Memoria por Referencia con Puntero Doble ⭐⭐☆☆☆¶
Solution to Exercise punteros-adv-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#include <assert.h> #include <stdbool.h> #include <stddef.h> #include <stdlib.h> #include <string.h> bool alocar_cadena(char **puntero_a_cadena, const char *valor_inicial) { if (puntero_a_cadena == NULL || valor_inicial == NULL) { return false; } size_t len = strlen(valor_inicial); char *nueva = malloc(len + 1); if (nueva == NULL) { *puntero_a_cadena = NULL; return false; } memcpy(nueva, valor_inicial, len + 1); *puntero_a_cadena = nueva; return true; } int main(void) { char *texto = NULL; assert(alocar_cadena(&texto, "Hola Mundo") == true); assert(texto != NULL); assert(strcmp(texto, "Hola Mundo") == 0); free(texto); texto = NULL; assert(alocar_cadena(&texto, "") == true); assert(texto != NULL); assert(strcmp(texto, "") == 0); free(texto); texto = NULL; assert(alocar_cadena(NULL, "Test") == false); assert(alocar_cadena(&texto, NULL) == false); return 0; }
Ejercicio 4.02.02 - Desasignación Segura de Arreglo de Punteros ⭐⭐⭐☆☆¶
Solution to Exercise punteros-adv-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#include <assert.h> #include <stddef.h> #include <stdlib.h> #include <string.h> char **crear_arreglo_cadenas(size_t cantidad) { if (cantidad == 0) { return NULL; } char **arr = calloc(cantidad, sizeof(*arr)); return arr; } void liberar_tokens(char **tokens, size_t cantidad) { if (tokens == NULL) { return; } for (size_t i = 0; i < cantidad; i++) { free(tokens[i]); } free(tokens); } int main(void) { char **tokens = crear_arreglo_cadenas(3); assert(tokens != NULL); tokens[0] = malloc(10); strcpy(tokens[0], "Token1"); tokens[1] = NULL; // Elemento nulo defensivo tokens[2] = malloc(10); strcpy(tokens[2], "Token3"); liberar_tokens(tokens, 3); liberar_tokens(NULL, 0); // No debe fallar return 0; }
Punteros a Función: Callbacks y Genericidad¶
Ejercicio 4.02.03 - Orden Superior: Calculadora Parametrizada ⭐⭐☆☆☆¶
Solution to Exercise punteros-adv-3
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 <stddef.h> typedef int (*operacion_aritmetica_t)(int, int); int sumar(int a, int b) { return a + b; } int restar(int a, int b) { return a - b; } int multiplicar(int a, int b) { return a * b; } int ejecutar_operacion(int a, int b, operacion_aritmetica_t op) { assert(op != NULL); return op(a, b); } int main(void) { assert(ejecutar_operacion(10, 5, sumar) == 15); assert(ejecutar_operacion(10, 5, restar) == 5); assert(ejecutar_operacion(10, 5, multiplicar) == 50); return 0; }
Ejercicio 4.02.04 - Tabla de Despacho (Dispatch Table) ⭐⭐⭐☆☆¶
Solution to Exercise punteros-adv-4
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#include <assert.h> #include <stdbool.h> #include <stddef.h> typedef int (*operacion_aritmetica_t)(int, int); static int op_sumar(int a, int b) { return a + b; } static int op_restar(int a, int b) { return a - b; } static int op_multiplicar(int a, int b) { return a * b; } static const operacion_aritmetica_t TABLA_DESPACHO[] = { op_sumar, op_restar, op_multiplicar }; #define CANTIDAD_OPERACIONES (sizeof(TABLA_DESPACHO) / sizeof(TABLA_DESPACHO[0])) bool despachar_operacion(size_t codigo, int a, int b, int *resultado) { if (resultado == NULL || codigo >= CANTIDAD_OPERACIONES) { return false; } *resultado = TABLA_DESPACHO[codigo](a, b); return true; } int main(void) { int res = 0; assert(despachar_operacion(0, 20, 10, &res) == true && res == 30); assert(despachar_operacion(1, 20, 10, &res) == true && res == 10); assert(despachar_operacion(2, 20, 10, &res) == true && res == 200); // Caso fuera de rango assert(despachar_operacion(99, 20, 10, &res) == false); return 0; }
Ejercicio 4.02.05 - Mapeo Funcional In-Place sobre Arreglos ⭐⭐⭐☆☆¶
Solution to Exercise punteros-adv-5
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 <assert.h> #include <stddef.h> static int transformar_duplicar(int x) { return x * 2; } static int transformar_signo(int x) { return -x; } void mapear_arreglo(int *arr, size_t n, int (*transformar)(int)) { assert(transformar != NULL); if (arr == NULL || n == 0) { return; } for (size_t i = 0; i < n; i++) { arr[i] = transformar(arr[i]); } } int main(void) { int arr1[3] = {1, 2, 3}; mapear_arreglo(arr1, 3, transformar_duplicar); assert(arr1[0] == 2 && arr1[1] == 4 && arr1[2] == 6); int arr2[3] = {5, -10, 0}; mapear_arreglo(arr2, 3, transformar_signo); assert(arr2[0] == -5 && arr2[1] == 10 && arr2[2] == 0); return 0; }
Ejercicio 4.02.06 - Ordenamiento Genérico con Comparador (Tipo qsort) ⭐⭐⭐⭐☆¶
Solution to Exercise punteros-adv-6
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#include <assert.h> #include <stddef.h> #include <stdlib.h> #include <string.h> typedef int (*comparador_t)(const void *, const void *); void ordenar_generico(void *base, size_t n, size_t tam_elem, comparador_t cmp) { assert(cmp != NULL); assert(tam_elem > 0); if (base == NULL || n < 2) { return; } char *bytes = (char *)base; void *temp = malloc(tam_elem); if (temp == NULL) { return; } for (size_t i = 0; i < n - 1; i++) { for (size_t j = 0; j < n - 1 - i; j++) { char *elem_a = bytes + (j * tam_elem); char *elem_b = bytes + ((j + 1) * tam_elem); if (cmp(elem_a, elem_b) > 0) { memcpy(temp, elem_a, tam_elem); memcpy(elem_a, elem_b, tam_elem); memcpy(elem_b, temp, tam_elem); } } } free(temp); } static int cmp_enteros(const void *a, const void *b) { int val_a = *(const int *)a; int val_b = *(const int *)b; return (val_a > val_b) - (val_a < val_b); } static int cmp_doubles(const void *a, const void *b) { double val_a = *(const double *)a; double val_b = *(const double *)b; return (val_a > val_b) - (val_a < val_b); } int main(void) { int arr_int[4] = {5, 1, 4, 2}; ordenar_generico(arr_int, 4, sizeof(int), cmp_enteros); assert(arr_int[0] == 1 && arr_int[1] == 2 && arr_int[2] == 4 && arr_int[3] == 5); double arr_dbl[3] = {3.5, 1.2, 9.8}; ordenar_generico(arr_dbl, 3, sizeof(double), cmp_doubles); assert(arr_dbl[0] == 1.2 && arr_dbl[1] == 3.5 && arr_dbl[2] == 9.8); return 0; }
Ejercicio 4.02.07 - Reducción Funcional Genérica (Fold / Reduce) ⭐⭐⭐⭐☆¶
Solution to Exercise punteros-adv-7
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#include <assert.h> #include <math.h> #include <stddef.h> typedef void (*reductor_t)(void *acumulador, const void *elemento); void reducir_generico(const void *base, size_t n, size_t tam_elem, reductor_t f, void *acumulador) { assert(f != NULL); assert(acumulador != NULL); assert(tam_elem > 0); if (base == NULL || n == 0) { return; } const char *bytes = (const char *)base; for (size_t i = 0; i < n; i++) { const void *elem = bytes + (i * tam_elem); f(acumulador, elem); } } static void sumar_enteros(void *acum, const void *elem) { int *total = (int *)acum; const int *val = (const int *)elem; *total += *val; } static void maximo_doubles(void *acum, const void *elem) { double *max_val = (double *)acum; const double *val = (const double *)elem; if (*val > *max_val) { *max_val = *val; } } int main(void) { int arr_int[3] = {10, 20, 30}; int suma = 0; reducir_generico(arr_int, 3, sizeof(int), sumar_enteros, &suma); assert(suma == 60); double arr_dbl[3] = {1.5, 9.2, 4.3}; double maximo = -HUGE_VAL; reducir_generico(arr_dbl, 3, sizeof(double), maximo_doubles, &maximo); assert(maximo == 9.2); int preservado = 100; reducir_generico(NULL, 0, sizeof(int), sumar_enteros, &preservado); assert(preservado == 100); return 0; }
Ejercicio 4.02.08 - Máquina de Estados Finita (FSM) con Matriz de Callbacks ⭐⭐⭐⭐☆¶
Solution to Exercise punteros-adv-8
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#include <assert.h> #include <stddef.h> typedef enum { ESTADO_REPOSO = 0, ESTADO_EJECUCION, ESTADO_PAUSA, CANT_ESTADOS } estado_t; typedef enum { EVENTO_ARRANCAR = 0, EVENTO_PAUSAR, EVENTO_DETENER, CANT_EVENTOS } evento_t; typedef estado_t (*transicion_fn)(void); static estado_t a_ejecucion(void) { return ESTADO_EJECUCION; } static estado_t a_pausa(void) { return ESTADO_PAUSA; } static estado_t a_reposo(void) { return ESTADO_REPOSO; } static const transicion_fn TABLA_FSM[CANT_ESTADOS][CANT_EVENTOS] = { /* ESTADO_REPOSO */ [ESTADO_REPOSO] = { [EVENTO_ARRANCAR] = a_ejecucion, [EVENTO_PAUSAR] = NULL, [EVENTO_DETENER] = NULL }, /* ESTADO_EJECUCION */ [ESTADO_EJECUCION] = { [EVENTO_ARRANCAR] = NULL, [EVENTO_PAUSAR] = a_pausa, [EVENTO_DETENER] = a_reposo }, /* ESTADO_PAUSA */ [ESTADO_PAUSA] = { [EVENTO_ARRANCAR] = a_ejecucion, [EVENTO_PAUSAR] = NULL, [EVENTO_DETENER] = a_reposo } }; estado_t fsm_transicionar(estado_t actual, evento_t evento) { if (actual >= CANT_ESTADOS || evento >= CANT_EVENTOS) { return actual; } transicion_fn fn = TABLA_FSM[actual][evento]; if (fn == NULL) { return actual; } return fn(); } int main(void) { estado_t st = ESTADO_REPOSO; // Reposo -> Arrancar -> Ejecución st = fsm_transicionar(st, EVENTO_ARRANCAR); assert(st == ESTADO_EJECUCION); // Ejecución -> Pausar -> Pausa st = fsm_transicionar(st, EVENTO_PAUSAR); assert(st == ESTADO_PAUSA); // Pausa -> Arrancar -> Ejecución st = fsm_transicionar(st, EVENTO_ARRANCAR); assert(st == ESTADO_EJECUCION); // Ejecución -> Detener -> Reposo st = fsm_transicionar(st, EVENTO_DETENER); assert(st == ESTADO_REPOSO); // Transición no permitida: Reposo no puede pausarse estado_t mismo = fsm_transicionar(ESTADO_REPOSO, EVENTO_PAUSAR); assert(mismo == ESTADO_REPOSO); // Evento fuera de rango assert(fsm_transicionar(ESTADO_EJECUCION, (evento_t)99) == ESTADO_EJECUCION); assert(fsm_transicionar((estado_t)99, EVENTO_ARRANCAR) == (estado_t)99); return 0; }
Ejercicio 4.02.09 - Filtrado Funcional Genérico In-Place ⭐⭐⭐⭐☆¶
Solution to Exercise punteros-avanzados-9
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#include <stdio.h> #include <stddef.h> #include <stdbool.h> #include <string.h> #include <assert.h> size_t filtrar_generico(void *base, size_t nmemb, size_t size, bool (*predicado)(const void *)) { if (base == NULL || nmemb == 0 || size == 0 || predicado == NULL) { return 0; } char *bytes = (char *)base; size_t retenidos = 0; for (size_t i = 0; i < nmemb; i++) { char *actual = bytes + (i * size); if (predicado(actual)) { char *destino = bytes + (retenidos * size); if (destino != actual) { memcpy(destino, actual, size); } retenidos++; } } return retenidos; } static bool es_par(const void *elem) { const int *val = (const int *)elem; return (*val % 2) == 0; } static bool es_cadena_no_vacia(const void *elem) { const char *const *str = (const char *const *)elem; return (*str != NULL && strlen(*str) > 0); } int main(void) { // 1. Filtrado de enteros pares int numeros[6] = {1, 2, 3, 4, 5, 6}; size_t cant_pares = filtrar_generico(numeros, 6, sizeof(int), es_par); assert(cant_pares == 3); assert(numeros[0] == 2); assert(numeros[1] == 4); assert(numeros[2] == 6); // 2. Ninguno cumple el predicado int impares[3] = {1, 3, 5}; assert(filtrar_generico(impares, 3, sizeof(int), es_par) == 0); // 3. Todos cumplen el predicado int pares[3] = {2, 4, 6}; assert(filtrar_generico(pares, 3, sizeof(int), es_par) == 3); assert(pares[0] == 2 && pares[1] == 4 && pares[2] == 6); // 4. Filtrado de arreglo de cadenas (char *) const char *palabras[4] = {"alpha", "", "beta", ""}; size_t cant_str = filtrar_generico(palabras, 4, sizeof(const char *), es_cadena_no_vacia); assert(cant_str == 2); assert(strcmp(palabras[0], "alpha") == 0); assert(strcmp(palabras[1], "beta") == 0); // 5. Casos nulos y defensivos assert(filtrar_generico(NULL, 10, sizeof(int), es_par) == 0); assert(filtrar_generico(numeros, 6, sizeof(int), NULL) == 0); return 0; }