Prerrequisitos y Entorno de Ejecución Requerido¶
Para compilar y verificar las soluciones de este módulo bajo el estándar C11 estricto de cátedra, se requiere:
Compilador C11: GCC 9+ o Clang 11+ configurado con flags
-Wall -Wextra -Werror -pedantic -std=c11.Entorno POSIX: Linux o WSL con utilidades estándar y análisis de memoria.
Herramientas de Verificación: Valgrind (memcheck) y AddressSanitizer (
-fsanitize=address,undefined) para garantizar la correcta alineación y ausencia de lectura de memoria no inicializada.Conocimientos Previos: Tipos heterogéneos, palabra clave
struct, aliastypedef, operadores punto (.) y flecha (->), y paso por puntero constante (const tipo_t *).
Objetivos Pedagógicos y Competencias (Taxonomía de Bloom)¶
Nivel 2 (Comprensión): Analizar el empaquetado de memoria en estructuras heterogéneas, padding y alineación de bytes.
Nivel 3 (Aplicación): Implementar modelos de datos compuestos, inicialización segura con llaves y pasaje eficiente por referencia.
Nivel 4 (Análisis): Evaluar el costo de copia de estructuras en la pila frente a la indirección por puntero.
Andamiaje Progresivo: Ejercicios andamiados con contratos formales (precondiciones/postcondiciones), tablas de vectores de prueba y suites ejecutables con
assert().
Capítulos de Apunte Correspondientes¶
Cuestiones de Estilo Aplicables¶
Inicialización de structs: Inicializá siempre las estructuras utilizando llaves en su declaración (ej.
fraccion_t f = {0, 1};) para evitar basura en sus miembros (ver 0x7001h: Siempre debés inicializar las variables a un valor conocido).Paso por puntero constante: Para evitar el overhead de copiar estructuras por valor en la pila de llamadas, pasá punteros a estructuras constantes (
const tipo_t *) cuando la función solo lea los campos.Acceso a miembros: Utilizá la sintaxis de operador flecha (
p->campo) cuando se opere sobre punteros a estructuras, evitando la indirección parentizada(*p).campo.
Modelado Matemático: Fracciones¶
Ejercicio 2.07.01 - Operaciones Aritméticas con Fracciones ⭐⭐☆☆☆¶
Solution to Exercise structs-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#include <assert.h> #include <stddef.h> typedef struct { int numerador; int denominador; } fraccion_t; fraccion_t fraccion_sumar(fraccion_t a, fraccion_t b) { assert(a.denominador != 0); assert(b.denominador != 0); fraccion_t res; res.numerador = (a.numerador * b.denominador) + (b.numerador * a.denominador); res.denominador = a.denominador * b.denominador; return res; } fraccion_t fraccion_multiplicar(fraccion_t a, fraccion_t b) { assert(a.denominador != 0); assert(b.denominador != 0); fraccion_t res; res.numerador = a.numerador * b.numerador; res.denominador = a.denominador * b.denominador; return res; } int main(void) { fraccion_t f1 = {1, 2}; fraccion_t f2 = {1, 3}; // Caso normal fraccion_t suma = fraccion_sumar(f1, f2); assert(suma.numerador == 5 && suma.denominador == 6); fraccion_t prod = fraccion_multiplicar(f1, f2); assert(prod.numerador == 1 && prod.denominador == 6); // Caso numerador cero fraccion_t f_cero = {0, 1}; fraccion_t f3 = {3, 4}; fraccion_t suma_cero = fraccion_sumar(f_cero, f3); assert(suma_cero.numerador == 3 && suma_cero.denominador == 4); fraccion_t prod_cero = fraccion_multiplicar(f_cero, f3); assert(prod_cero.numerador == 0 && prod_cero.denominador == 4); return 0; }
Ejercicio 2.07.02 - Simplificación de Fracción mediante MCD ⭐⭐☆☆☆¶
Solution to Exercise structs-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#include <assert.h> #include <stdlib.h> typedef struct { int numerador; int denominador; } fraccion_t; static int calcular_mcd(int a, int b) { a = abs(a); b = abs(b); while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } fraccion_t fraccion_simplificar(fraccion_t f) { assert(f.denominador != 0); if (f.numerador == 0) { fraccion_t cero = {0, 1}; return cero; } int mcd = calcular_mcd(f.numerador, f.denominador); fraccion_t res; res.numerador = f.numerador / mcd; res.denominador = f.denominador / mcd; if (res.denominador < 0) { res.numerador = -res.numerador; res.denominador = -res.denominador; } return res; } int main(void) { // Caso simplificación normal fraccion_t f1 = {4, 8}; fraccion_t s1 = fraccion_simplificar(f1); assert(s1.numerador == 1 && s1.denominador == 2); // Caso ya irreducible fraccion_t f2 = {7, 11}; fraccion_t s2 = fraccion_simplificar(f2); assert(s2.numerador == 7 && s2.denominador == 11); // Caso cero fraccion_t f3 = {0, 5}; fraccion_t s3 = fraccion_simplificar(f3); assert(s3.numerador == 0 && s3.denominador == 1); // Caso normalizar signo denominador fraccion_t f4 = {3, -6}; fraccion_t s4 = fraccion_simplificar(f4); assert(s4.numerador == -1 && s4.denominador == 2); // Caso doble negativo fraccion_t f5 = {-4, -8}; fraccion_t s5 = fraccion_simplificar(f5); assert(s5.numerador == 1 && s5.denominador == 2); return 0; }
Retorno Múltiple Encapsulado¶
Ejercicio 2.07.03 - Estructura de División Entera Lenta ⭐⭐⭐☆☆¶
Solution to Exercise structs-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#include <assert.h> typedef struct { int cociente; int resto; } division_t; division_t division_lenta(int dividendo, int divisor) { assert(dividendo >= 0); assert(divisor > 0); division_t resultado; resultado.cociente = 0; resultado.resto = dividendo; while (resultado.resto >= divisor) { resultado.resto -= divisor; resultado.cociente++; } return resultado; } int main(void) { // Caso con resto division_t d1 = division_lenta(14, 3); assert(d1.cociente == 4 && d1.resto == 2); // Caso exacta division_t d2 = division_lenta(15, 5); assert(d2.cociente == 3 && d2.resto == 0); // Caso dividendo menor que divisor division_t d3 = division_lenta(2, 5); assert(d3.cociente == 0 && d3.resto == 2); // Caso dividendo cero division_t d4 = division_lenta(0, 7); assert(d4.cociente == 0 && d4.resto == 0); // Caso iguales division_t d5 = division_lenta(9, 9); assert(d5.cociente == 1 && d5.resto == 0); return 0; }
Normalización y Acarreo de Unidades¶
Ejercicio 2.07.04 - Normalización y Suma de Tiempos ⭐⭐☆☆☆¶
Solution to Exercise structs-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 43 44 45 46 47 48 49 50 51 52#include <assert.h> typedef struct { int horas; int minutos; int segundos; } tiempo_t; tiempo_t sumar_tiempos(tiempo_t t1, tiempo_t t2) { tiempo_t res; int total_segundos = t1.segundos + t2.segundos; int acarreo_minutos = total_segundos / 60; res.segundos = total_segundos % 60; int total_minutos = t1.minutos + t2.minutos + acarreo_minutos; int acarreo_horas = total_minutos / 60; res.minutos = total_minutos % 60; res.horas = t1.horas + t2.horas + acarreo_horas; return res; } int main(void) { // Sin acarreo tiempo_t t1 = {1, 20, 15}; tiempo_t t2 = {2, 10, 20}; tiempo_t r1 = sumar_tiempos(t1, t2); assert(r1.horas == 3 && r1.minutos == 30 && r1.segundos == 35); // Acarreo segundos tiempo_t t3 = {0, 10, 45}; tiempo_t t4 = {0, 5, 30}; tiempo_t r2 = sumar_tiempos(t3, t4); assert(r2.horas == 0 && r2.minutos == 16 && r2.segundos == 15); // Acarreo minutos tiempo_t t5 = {1, 50, 0}; tiempo_t t6 = {2, 30, 0}; tiempo_t r3 = sumar_tiempos(t5, t6); assert(r3.horas == 4 && r3.minutos == 20 && r3.segundos == 0); // Acarreo en cascada tiempo_t t7 = {0, 59, 59}; tiempo_t t8 = {0, 0, 1}; tiempo_t r4 = sumar_tiempos(t7, t8); assert(r4.horas == 1 && r4.minutos == 0 && r4.segundos == 0); return 0; }
Paso por Referencia y Operador Flecha (->)¶
Ejercicio 2.07.05 - Traslación Espacial de Punto Cartesiano ⭐⭐☆☆☆¶
Solution to Exercise structs-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 33 34 35#include <assert.h> #include <stddef.h> typedef struct { int x; int y; } punto_t; void trasladar(punto_t *p, int dx, int dy) { assert(p != NULL); p->x += dx; p->y += dy; } int main(void) { punto_t pt = {10, 20}; // Caso normal trasladar(&pt, 5, -3); assert(pt.x == 15 && pt.y == 17); // Caso desplazamiento nulo trasladar(&pt, 0, 0); assert(pt.x == 15 && pt.y == 17); // Caso negativos punto_t neg = {-10, -10}; trasladar(&neg, -5, -5); assert(neg.x == -15 && neg.y == -15); return 0; }
Ejercicio 2.07.06 - Ordenamiento de Arreglo de Puntos por Coordenada X ⭐⭐⭐☆☆¶
Solution to Exercise structs-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#include <assert.h> #include <stdbool.h> #include <stddef.h> typedef struct { int x; int y; } punto_t; static bool es_mayor(punto_t a, punto_t b) { if (a.x != b.x) { return a.x > b.x; } return a.y > b.y; } void ordenar_puntos_por_x(punto_t *puntos, size_t n) { if (puntos == NULL || n < 2) { return; } for (size_t i = 0; i < n - 1; i++) { for (size_t j = 0; j < n - 1 - i; j++) { if (es_mayor(puntos[j], puntos[j + 1])) { punto_t aux = puntos[j]; puntos[j] = puntos[j + 1]; puntos[j + 1] = aux; } } } } int main(void) { // Caso normal punto_t pts1[3] = {{5, 2}, {1, 9}, {3, 4}}; ordenar_puntos_por_x(pts1, 3); assert(pts1[0].x == 1 && pts1[1].x == 3 && pts1[2].x == 5); // Caso empate en X punto_t pts2[3] = {{2, 8}, {2, 3}, {2, 5}}; ordenar_puntos_por_x(pts2, 3); assert(pts2[0].y == 3 && pts2[1].y == 5 && pts2[2].y == 8); // Caso un elemento punto_t pts3[1] = {{10, 20}}; ordenar_puntos_por_x(pts3, 1); assert(pts3[0].x == 10 && pts3[0].y == 20); return 0; }
Ejercicio 2.07.07 - Encapsulamiento de Arreglo Seguro con Longitud y Capacidad ⭐⭐⭐☆☆¶
Solution to Exercise structs-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#include <assert.h> #include <stdbool.h> #include <stddef.h> #define CAPACIDAD_MAX 8 typedef struct { int datos[CAPACIDAD_MAX]; size_t longitud; } arreglo_seguro_t; void arreglo_inicializar(arreglo_seguro_t *arr) { assert(arr != NULL); arr->longitud = 0; } bool arreglo_insertar(arreglo_seguro_t *arr, int valor) { assert(arr != NULL); if (arr->longitud >= CAPACIDAD_MAX) { return false; } arr->datos[arr->longitud] = valor; arr->longitud++; return true; } int main(void) { arreglo_seguro_t mi_arr; arreglo_inicializar(&mi_arr); assert(mi_arr.longitud == 0); // Inserción normal assert(arreglo_insertar(&mi_arr, 10) == true); assert(arreglo_insertar(&mi_arr, 20) == true); assert(mi_arr.longitud == 2); assert(mi_arr.datos[0] == 10 && mi_arr.datos[1] == 20); // Llenar hasta capacidad máxima (8 elementos) for (int i = 2; i < CAPACIDAD_MAX; i++) { assert(arreglo_insertar(&mi_arr, i * 10) == true); } assert(mi_arr.longitud == CAPACIDAD_MAX); // Intento de desbordamiento (debe fallar limpiamente) assert(arreglo_insertar(&mi_arr, 999) == false); assert(mi_arr.longitud == CAPACIDAD_MAX); return 0; }
Ejercicio 2.07.08 - Búsqueda Lineal en Arreglo de Estructuras por Clave ⭐⭐☆☆☆¶
---
(ej_b2_c07_09)=
### Ejercicio 2.07.09 - Ordenamiento de Arreglo de Estructuras por Fecha Cronológica ⭐⭐⭐☆☆
:::{exercise}
:label: ordenar_eventos_cronologico
:enumerator: structs-9
En sistemas de auditoría y bitácoras de eventos (*logging*), los registros deben procesarse en estricto orden cronológico.
Dadas las siguientes estructuras:
```c
typedef struct {
int anio;
int mes;
int dia;
} fecha_t;
typedef struct {
int id;
fecha_t fecha;
} evento_t;
```
Implementá dos funciones:
1. `int comparar_fechas(fecha_t a, fecha_t b)`: retorna un entero negativo si $a$ es anterior a $b$, cero si son iguales, o positivo si $a$ es posterior a $b$.
2. `void ordenar_eventos_cronologico(evento_t *eventos, size_t n)`: ordena in-place el arreglo `eventos` de tamaño $n$ en orden cronológico ascendente empleando el algoritmo de ordenamiento por inserción. Si dos eventos coinciden en fecha, deben conservar su orden relativo original (estabilidad).
**Nivel de Bloom:** Nivel 3 (Aplicación) y Nivel 4 (Análisis).
**Conceptos requeridos:** Structs anidados, composición de comparadores, ordenamiento por inserción in-place, estabilidad algorítmica.
#### Tabla de Vectores de Prueba Obligatorios
| Caso de Prueba | Arreglo de Entrada ($n=3$) | Arreglo Resultante | Justificación Técnica |
| :--- | :--- | :--- | :--- |
| **Años Distintos** | `{{1, {2025, 5, 10}}, {2, {2023, 1, 1}}, {3, {2024, 12, 31}}}` | IDs: `2, 3, 1` | Orden por componente de mayor jerarquía |
| **Mismo Año y Mes** | `{{1, {2024, 3, 20}}, {2, {2024, 3, 5}}, {3, {2024, 3, 15}}}` | IDs: `2, 3, 1` | Desempate por componente día |
| **Estabilidad** | `{{10, {2024, 1, 1}}, {20, {2024, 1, 1}}}` | IDs: `10, 20` | Conservación del orden relativo ante fechas idénticas |
Solution to ordenar_eventos_cronologico
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#include <stdio.h> #include <stddef.h> #include <assert.h> typedef struct { int anio; int mes; int dia; } fecha_t; typedef struct { int id; fecha_t fecha; } evento_t; int comparar_fechas(fecha_t a, fecha_t b) { if (a.anio != b.anio) { return a.anio - b.anio; } if (a.mes != b.mes) { return a.mes - b.mes; } return a.dia - b.dia; } void ordenar_eventos_cronologico(evento_t *eventos, size_t n) { if (eventos == NULL || n <= 1) { return; } for (size_t i = 1; i < n; i++) { evento_t clave = eventos[i]; size_t j = i; while (j > 0 && comparar_fechas(eventos[j - 1].fecha, clave.fecha) > 0) { eventos[j] = eventos[j - 1]; j--; } eventos[j] = clave; } } int main(void) { evento_t lista1[3] = { {1, {2025, 5, 10}}, {2, {2023, 1, 1}}, {3, {2024, 12, 31}} }; ordenar_eventos_cronologico(lista1, 3); assert(lista1[0].id == 2); assert(lista1[1].id == 3); assert(lista1[2].id == 1); evento_t lista2[3] = { {1, {2024, 3, 20}}, {2, {2024, 3, 5}}, {3, {2024, 3, 15}} }; ordenar_eventos_cronologico(lista2, 3); assert(lista2[0].id == 2); assert(lista2[1].id == 3); assert(lista2[2].id == 1); // Verificación de estabilidad con fechas iguales evento_t lista3[2] = { {10, {2024, 1, 1}}, {20, {2024, 1, 1}} }; ordenar_eventos_cronologico(lista3, 2); assert(lista3[0].id == 10); assert(lista3[1].id == 20); // Arreglo vacío o nulo ordenar_eventos_cronologico(NULL, 0); return 0; }
Ejercicio 2.07.10 - Auditoría de Padding y Reordenamiento de Campos en Memoria ⭐⭐⭐☆☆¶
Solution to Exercise structs-10
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#include <stdio.h> #include <stddef.h> #include <assert.h> struct telemetria_desalineada { char flag_inicio; double voltaje; char flag_alerta; int codigo_error; }; struct telemetria_optimizada { double voltaje; int codigo_error; char flag_inicio; char flag_alerta; }; size_t calcular_bytes_desperdiciados(size_t tam_struct, size_t suma_miembros) { if (tam_struct < suma_miembros) { return 0; } return tam_struct - suma_miembros; } size_t calcular_ahorro_porcentaje(size_t tam_original, size_t tam_optimizado) { if (tam_original == 0 || tam_original < tam_optimizado) { return 0; } size_t delta = tam_original - tam_optimizado; return (delta * 100) / tam_original; } int main(void) { size_t suma_util = sizeof(char) + sizeof(double) + sizeof(char) + sizeof(int); assert(suma_util == 14); size_t tam_desal = sizeof(struct telemetria_desalineada); size_t tam_opt = sizeof(struct telemetria_optimizada); // En arquitecturas estándar de 64 bits con alineación de double a 8 bytes: assert(tam_desal >= 24); assert(tam_opt <= 16); assert(tam_opt < tam_desal); size_t pad_desal = calcular_bytes_desperdiciados(tam_desal, suma_util); size_t pad_opt = calcular_bytes_desperdiciados(tam_opt, suma_util); assert(pad_desal >= 10); assert(pad_opt <= 2); size_t ahorro = calcular_ahorro_porcentaje(tam_desal, tam_opt); assert(ahorro >= 33); // Ahorro de al menos 33% de memoria // Verificación de offsets y alineación assert(offsetof(struct telemetria_optimizada, voltaje) == 0); assert(offsetof(struct telemetria_optimizada, codigo_error) == sizeof(double)); // Casos borde assert(calcular_bytes_desperdiciados(10, 15) == 0); assert(calcular_ahorro_porcentaje(0, 10) == 0); return 0; }