Skip to article frontmatterSkip to article content

Ejercicios de structs

Ejercicios para modelar datos complejos utilizando estructuras (struct), desde conceptos básicos hasta aplicaciones más avanzadas en modelado de datos y algoritmos.

1: Fracciones

Desarrollar un tipo fraccion_t y las funciones necesarias para operar con fracciones. El objetivo es encapsular el numerador y el denominador en una única entidad.

typedef struct {
    int numerador;
    int denominador;
} fraccion_t;

1.1: Operaciones Aritméticas

Implementar funciones que tomen dos fracciones y retornen el resultado de la operación.

1.2: Simplificación

Implementar una función que modifique una fracción para llevarla a su mínima expresión. La simplificación se realiza dividiendo numerador y denominador por su Máximo Común Divisor (MCD).

2: División con Resto

La división entera en C (/ y %) produce dos resultados. El objetivo es unificar estas operaciones en una única función que retorne una struct con el cociente y el resto, basándose en el algoritmo de la división.

dividendo=divisor×cociente+resto,donde 0resto<divisordividendo = divisor \times cociente + resto, \quad \text{donde } 0 \le resto < |divisor|
typedef struct {
    int cociente;
    int resto;
} division_t;

division_t division_lenta(int dividendo, int divisor);

3: Medición de Tiempo

Desarrollar una estructura tiempo_t para representar un instante o duración en horas, minutos y segundos, junto con funciones para manipularla.

typedef struct {
    int horas;
    int minutos;
    int segundos;
} tiempo_t;

3.1: Suma de Tiempos

Implementar una función que sume dos tiempo_t. El resultado debe ser normalizado para que los segundos y minutos no excedan 59.

3.2: Comparación de Tiempos

Implementar una función que compare dos tiempo_t y devuelva un valor que indique si el primero es anterior, igual o posterior al segundo. Una estrategia es convertir ambos tiempos a una unidad común (ej. segundos totales) para facilitar la comparación.

ten_segundos=horas×3600+minutos×60+segundost_{en\_segundos} = horas \times 3600 + minutos \times 60 + segundos

4: Tipos de Datos Compuestos

4.1: Arreglos con Capacidad

Encapsular un arreglo en una estructura para agrupar el contenedor de datos, su longitud actual y su capacidad máxima. Esto previene errores de desbordamiento de búfer y simplifica pasar la información del arreglo a funciones.

#define CAPACIDAD_MAX 100
typedef struct {
    int datos[CAPACIDAD_MAX];
    size_t longitud; // Elementos actualmente en uso
} arreglo_t;

Este ejercicio puede ser mejorado con memoria dinámica, para lo cual, es necesario agregar un atributo mas con el tamaño en memoria pedido para datos.

4.2: Cadenas Seguras II

Aplicar el mismo principio a las cadenas de caracteres para crear un tipo cadena_segura_t que gestione su propia capacidad y longitud, evitando los peligros asociados a las cadenas de C estándar.

#define CAPACIDAD_MAX_CADENA 256
typedef struct {
    char datos[CAPACIDAD_MAX_CADENA];
    size_t longitud;
} cadena_segura_t;

5: Modelado de Datos del Mundo Real

5.1: Punto y Círculo

Definir estructuras para representar un punto en un plano 2D y un círculo. Implementar funciones geométricas básicas.

Funciones:

5.2: Rectángulo

Definir un rectángulo mediante dos puntos opuestos (esquina superior izquierda e inferior derecha). Implementar funciones para calcular su área y perímetro.

Fórmulas:

5.3: Estudiante y Curso

Modelar entidades de un dominio académico.

Funciones:

5.4: Vector 3D

Definir una estructura para un vector en el espacio 3D y sus operaciones fundamentales.

Operaciones para vectores V1(x1,y1,z1)V_1(x_1, y_1, z_1) y V2(x2,y2,z2)V_2(x_2, y_2, z_2):

5.5: Color RGB

Definir un color con componentes Rojo, Verde y Azul. Implementar una función que mezcle dos colores.

Función de mezcla (promedio):

5.6: Libro de Biblioteca

Simular una pequeña biblioteca. Definir una estructura para un libro y gestionar una colección.

Funcionalidades:

5.7: Fecha

Definir una estructura para una fecha y una función que la incremente en un día, manejando meses y años bisiestos.

Regla de año bisiesto: Un año es bisiesto si es divisible por 4, a menos que sea divisible por 100 pero no por 400.

((anio%4==0)(anio%1000))(anio%400==0)((anio \% 4 == 0) \land (anio \% 100 \neq 0)) \lor (anio \% 400 == 0)

5.8: Polinomio

Representar un polinomio y evaluarlo eficientemente.

Función de evaluación (Método de Horner): Para un polinomio P(x)=anxn++a1x+a0P(x) = a_n x^n + \dots + a_1 x + a_0, el método consiste en reescribirlo como:

P(x)=a0+x(a1+x(a2++x(an1+anx)))P(x) = a_0 + x(a_1 + x(a_2 + \dots + x(a_{n-1} + a_n x)\dots))

Esto reduce el número de multiplicaciones y es numéricamente más estable.

5.9: Número Complejo

Definir una estructura para números complejos y sus operaciones aritméticas.

Operaciones para C1=a+biC_1 = a+bi y C2=c+diC_2 = c+di:

5.10: Inventario de Producto

Modelar un producto para un sistema de inventario.

Programa de gestión: Crear un programa que use un arreglo de producto_t para:

5.11: struct Anidada (Dirección)

Demostrar la composición de structs anidando una dentro de otra.

Función: Escribir una función que reciba una persona_t e imprima todos sus datos, incluyendo la dirección completa.

5.12: Campos de Bits (Bitfields)

Utilizar campos de bits para empaquetar datos de forma eficiente a nivel de bits, ideal para representar flags o estados con memoria mínima.

Ejercicio: Definir una struct que use campos de bits para almacenar 8 banderas booleanas (flags) en un solo unsigned char. Demostrar cómo establecer, limpiar y verificar el estado de un bit individual.

5.13: Uniones (union)

Explorar el uso de uniones para que una variable pueda almacenar diferentes tipos de datos en el mismo espacio de memoria.

Ejercicio: Definir una union valor_t que pueda ser un int, float o char*. Crear una struct variable_t que contenga un enum para el tipo y la union valor_t. Esto se conoce como “unión etiquetada” y permite un polimorfismo rudimentario en C.

5.14: typedef con struct Anónima

Mostrar una forma común y concisa de definir un tipo de struct usando typedef directamente con una struct anónima.

Ejercicio: Definir un tipo punto_t usando typedef struct { double x, y; } punto_t;. Comparar esta sintaxis con la alternativa de dos pasos (struct punto_t { ... }; typedef struct punto_t punto_t;).

5.15: Comparación de structs

Implementar una función para comparar si dos structs son idénticas.

Ejercicio: Implementar bool son_iguales(persona_t p1, persona_t p2) (usando la struct del ejercicio 5.11). La igualdad requiere que todos los campos, incluyendo los de la struct anidada direccion_t, sean idénticos.

6: Más Estructuras de Datos y Algoritmos

6.1: Mazo de Cartas

Modelar un mazo de cartas de póker.

Funciones:

6.2: Nodo de Lista Enlazada

Implementar la estructura de datos fundamental para una lista enlazada simple.

Funciones básicas:

6.3: Nodo de Árbol Binario

Implementar la estructura de un nodo para un árbol binario.

Funciones para un Árbol de Búsqueda Binaria (BST):

6.4: Transacción Bancaria

Modelar una cuenta bancaria con un historial de transacciones de tamaño dinámico.

Función clave: Implementar una función que agregue una transacción a una cuenta, actualice el saldo y use realloc para agrandar el arreglo del historial de transacciones.

6.5: Partícula en Simulación Física

Modelar una partícula simple para una simulación física.

Función de actualización (Integración de Euler): Implementar actualizar_estado(particula_t* p, double dt) que actualice la posición y velocidad después de un intervalo de tiempo dt.

6.6: Agenda de Contactos

Gestionar una agenda de contactos usando un arreglo dinámico.

Programa de gestión: Crear un programa que permita añadir, buscar por nombre y eliminar contactos de un arreglo dinámico de contacto_t, usando realloc para ajustar su tamaño.

6.7: Alineación de Structs (#pragma pack)

Investigar cómo el compilador organiza los miembros de una struct en memoria y el impacto en su tamaño total (sizeof).

Ejercicio: Crear una struct con miembros char, int, y char. Imprimir su sizeof. Luego, reordenar los miembros para agruparlos por tamaño o usar la directiva #pragma pack(1) y volver a imprimir el tamaño para observar cómo el padding (relleno) de memoria es eliminado o reducido.

6.8: Arreglos de Longitud Flexible

Explorar la característica de C99 que permite declarar un arreglo como último miembro de una struct sin un tamaño fijo.

Ejercicio: Definir struct texto_t { size_t longitud; char contenido[]; }. Escribir una función que reserve memoria para esta struct más N caracteres adicionales para el contenido, demostrando cómo crear objetos de tamaño variable.

6.9: Asignación de Structs y Copia Superficial

Demostrar el comportamiento por defecto de la asignación de structs que contienen punteros.

Ejercicio: Crear una struct que contenga un char*. Asignar memoria para la struct y para el puntero (malloc). Luego, crear una segunda struct y asignarle la primera (struct_b = struct_a;). Modificar la cadena a través de struct_b y mostrar que el cambio se refleja en struct_a, evidenciando que solo se copió el puntero (copia superficial), no los datos a los que apunta.

6.10: Puntero a Función en una Struct

Usar punteros a funciones dentro de structs para asociar datos con comportamiento (un pilar de la programación orientada a objetos).

Ejercicio: Definir typedef int (*operacion_func)(int, int);. Crear struct operacion_t { char simbolo; operacion_func func; }. Implementar un arreglo de operacion_t para una calculadora, donde cada elemento contiene el símbolo (+, -, etc.) y el puntero a la función correspondiente.