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.
Suma:
Resta:
Multiplicación:
Divisió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.
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.
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.
struct punto_t { double x, y; }struct circulo_t { punto_t centro; double radio; }
Funciones:
Distancia entre dos puntos y :
Punto dentro de un círculo: Un punto está dentro de un círculo si su distancia al centro es menor o igual al radio.
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.
struct rectangulo_t { punto_t sup_izq; punto_t inf_der; }
Fórmulas:
Ancho:
Alto:
Área:
Perímetro:
5.3: Estudiante y Curso¶
Modelar entidades de un dominio académico.
struct estudiante_t(nombre, legajo, promedio)struct curso_t(nombre, un arreglo de estudiantes, cantidad de estudiantes)
Funciones:
Añadir un estudiante a un curso (gestionando la capacidad del arreglo).
Encontrar al estudiante con el mejor promedio del curso.
Calcular el promedio general del curso:
5.4: Vector 3D¶
Definir una estructura para un vector en el espacio 3D y sus operaciones fundamentales.
struct vector3d_t { double x, y, z; }
Operaciones para vectores y :
Suma:
Resta:
Producto escalar:
Producto vectorial:
Magnitud (norma):
5.5: Color RGB¶
Definir un color con componentes Rojo, Verde y Azul. Implementar una función que mezcle dos colores.
struct color_rgb_t { unsigned char r, g, b; }
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.
struct libro_t(título, autor, ISBN, disponible)
Funcionalidades:
Buscar un libro por título o ISBN en un arreglo de
libro_t.Prestar un libro (cambiar su estado a no disponible).
Devolver un libro (cambiar su estado a disponible).
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.
struct fecha_t { int dia, mes, anio; }
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.
5.8: Polinomio¶
Representar un polinomio y evaluarlo eficientemente.
struct polinomio_t(grado, puntero a coeficientesdouble*)
Función de evaluación (Método de Horner): Para un polinomio , el método consiste en reescribirlo como:
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.
struct complejo_t { double real, imag; }
Operaciones para y :
Suma:
Resta:
Multiplicación:
División:
5.10: Inventario de Producto¶
Modelar un producto para un sistema de inventario.
struct producto_t(código, descripción, precio, stock)
Programa de gestión: Crear un programa que use un arreglo de producto_t para:
Añadir nuevos productos.
Buscar un producto por su código.
Actualizar el stock de un producto.
Eliminar un producto del inventario.
5.11: struct Anidada (Dirección)¶
Demostrar la composición de structs anidando una dentro de otra.
struct direccion_t(calle, numero, ciudad, codigo_postal)struct persona_t(nombre, edad,direccion_tdireccion)
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.
struct carta_t(palo, valor) dondepaloyvalorpueden serenum.struct mazo_t(un arreglo de 52carta_t).
Funciones:
crear_mazo(): Genera un mazo ordenado con las 52 cartas.barajar_mazo(mazo_t* mazo): Desordena las cartas aleatoriamente (ej. algoritmo de Fisher-Yates).repartir_carta(mazo_t* mazo): Devuelve la carta de arriba del mazo y actualiza el estado del mazo.
6.2: Nodo de Lista Enlazada¶
Implementar la estructura de datos fundamental para una lista enlazada simple.
struct nodo_t { int dato; struct nodo_t* siguiente; }
Funciones básicas:
insertar_al_principio(struct nodo_t** cabeza, int dato)imprimir_lista(struct nodo_t* cabeza)liberar_lista(struct nodo_t** cabeza)
6.3: Nodo de Árbol Binario¶
Implementar la estructura de un nodo para un árbol binario.
struct arbol_nodo_t { int dato; struct arbol_nodo_t* izq; struct arbol_nodo_t* der; }
Funciones para un Árbol de Búsqueda Binaria (BST):
insertar_nodo(struct arbol_nodo_t** raiz, int dato)imprimir_in_order(struct arbol_nodo_t* raiz)(recorrido en orden: izquierda, raíz, derecha)
6.4: Transacción Bancaria¶
Modelar una cuenta bancaria con un historial de transacciones de tamaño dinámico.
struct transaccion_t(fecha, tipo, monto)struct cuenta_t(nro_cuenta, saldo,transaccion_t*historial,size_tnum_transacciones)
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.
struct particula_t(posición, velocidad, aceleración, todas de tipovector3d_tdel ejercicio 5.4).
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.
struct contacto_t(nombre, telefono, email)
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.