vdf6d19d
Curso

11 - Estructuras (`struct`)

📋 Contenidos

Objetivos

Agrupar datos relacionados en C usando estructuras para modelar entidades con varios campos.

Contenidos

Qué problema resuelven las estructuras

Definición de struct

Declaración, inicialización y acceso a campos

Campos compuestos y arreglos de estructuras

Estructuras y funciones

Errores frecuentes y cierre

Material de Clase

Presentaciones

Práctico

Ver ejercicios

Los ejercicios de esta semana son acumulativos: todos trabajan sobre el mismo tipo de dato base. Cada ejercicio puede resolverse en un archivo separado, pero conviene reutilizar las ideas y funciones de los anteriores.

Usar esta definición en toda la práctica, salvo que el enunciado indique otra cosa:

struct Alumno {
    char nombre[20];
    int edad;
    float promedio;
};

Parte A: uso de estructuras en programas

Esta parte trabaja la definición de struct, el acceso a campos, el uso de arreglos de estructuras y funciones que reciben o devuelven estructuras.


Ejercicio 1: Primer alumno

Enunciado: Definir struct Alumno, declarar una variable e inicializarla con llaves. Luego imprimir sus tres campos en pantalla.

Ejemplo: Un alumno podría inicializarse como {"Ana", 19, 8.5}.


Ejercicio 2: Acceso y modificación de campos

Enunciado: Declarar dos variables de tipo struct Alumno, inicializarlas con llaves e imprimir sus datos. Luego modificar la edad y el promedio de una de ellas usando el operador . y volver a imprimirla.


Ejercicio 3: Mostrar un alumno con una función

Enunciado: Escribir una función que reciba un alumno e imprima su nombre, edad y promedio. En main, crear al menos dos alumnos y mostrarlos llamando a esa función.


Ejercicio 4: Crear un alumno con una función

Enunciado: Escribir una función que cree un alumno completo a partir de datos ingresados por teclado. La función debe pedir el nombre, la edad y el promedio, guardar esos datos en una variable local de tipo struct Alumno y devolver el alumno creado con return. En main, llamar a la función, guardar el resultado en una variable y mostrarlo con la función del ejercicio anterior.

Nota: Para leer el nombre, usar scanf("%19s", alumno.nombre).


Ejercicio 5: Actualizar el promedio

Enunciado: Escribir una función que reciba un alumno y un nuevo promedio. La función debe cambiar el campo promedio de la copia recibida y devolver el alumno actualizado. En main, crear un alumno, pedir un nuevo promedio, llamar a la función y mostrar el resultado antes y después de la actualización.

Pregunta para responder en un comentario: ¿Por qué la función debe devolver el alumno actualizado?


Ejercicio 6: Arreglo de alumnos inicializado

Enunciado: Declarar un arreglo de 3 estructuras struct Alumno e inicializarlo con llaves. Recorrer el arreglo con un for y mostrar cada alumno usando la función de impresión creada anteriormente.


Ejercicio 7: Cargar un grupo con una función

Enunciado: Declarar un arreglo de 4 elementos de tipo struct Alumno. Recorrerlo con un for y cargar cada posición llamando a la función que crea un alumno. Luego recorrerlo nuevamente para mostrar todos los alumnos con la función de impresión.


Ejercicio 8: Funciones sobre un arreglo de estructuras

Enunciado: Escribir una función que calcule el promedio general de un grupo de alumnos y otra función que cuente cuántos alumnos aprobaron.

La primera función debe recorrer el arreglo y devolver el promedio de los campos promedio de todos los alumnos. La segunda debe recorrer el arreglo y devolver cuántos alumnos tienen promedio mayor o igual a 6.0.

En main, cargar un grupo de 5 alumnos y mostrar ambos resultados.


Ejercicio 9: Alumno con mayor promedio

Enunciado: Escribir una función que reciba un grupo de alumnos y devuelva el alumno con mayor promedio. En main, cargar 5 alumnos, llamar a la función y mostrar el alumno devuelto.

Condición: Resolver la búsqueda con un for, comparando el campo promedio de cada estructura.


Ejercicio 10: Integrador y análisis de errores

Enunciado: Crear un programa que use las funciones de los ejercicios anteriores para:

  1. Cargar 5 alumnos.
  2. Mostrar todos los alumnos.
  3. Actualizar el promedio del primer alumno.
  4. Mostrar el promedio general del grupo.
  5. Mostrar cuántos alumnos aprobaron.
  6. Mostrar el alumno con mayor promedio.

Luego indicar qué error hay en cada fragmento y corregirlo:

  • struct Alumno a1; edad = 20;
  • struct Alumno grupo[5]; printf("%d", grupo.edad);
  • scanf("%f", alumno.promedio);
  • Llamar a la función que actualiza el promedio sin guardar el valor retornado.

Parte B: cálculo de tamaño, padding y alineación

Esta parte se resuelve principalmente en papel. El objetivo es calcular offsets, padding interno, padding final y tamaño total de estructuras. sizeof se usa solo para verificar los resultados.

Para los cálculos manuales, usar estas reglas simplificadas de una máquina típica:

  • char ocupa 1 byte y alinea a 1.
  • int ocupa 4 bytes y alinea a 4.
  • float ocupa 4 bytes y alinea a 4.
  • Un arreglo ocupa tantos bytes como la suma de sus elementos.
  • Cada campo empieza en un offset compatible con su alineación.
  • Una estructura anidada alinea según la mayor alineación de sus campos.
  • El tamaño total de la estructura se redondea a un múltiplo de la mayor alineación de sus campos.

Ejercicio 11: Medir el tamaño de una estructura

Enunciado: Escribir un programa que defina struct Alumno y muestre en pantalla:

  • sizeof(char)
  • sizeof(int)
  • sizeof(float)
  • sizeof(struct Alumno)

Luego calcular a mano cuánto ocuparían sus campos sumados directamente y comparar ese resultado con sizeof(struct Alumno).

Pregunta para responder en un comentario: En esta estructura aparece padding? Dónde?


Ejercicio 12: Calcular offsets paso a paso

Enunciado: Dada la siguiente estructura, calcular a mano el offset de cada campo, los bytes de padding y el tamaño total.

struct Sensor {
    char id;
    int lectura;
    char estado;
};

Luego escribir un programa que imprima sizeof(struct Sensor) para verificar el resultado.

Guía: Completar una tabla con estas columnas:

  • Campo
  • Offset donde empieza
  • Bytes que ocupa
  • Padding antes del campo

Ejercicio 13: Cambiar el orden de los campos

Enunciado: Comparar estas dos estructuras:

struct SensorA {
    char id;
    int lectura;
    char estado;
};

struct SensorB {
    int lectura;
    char id;
    char estado;
};

Calcular a mano el tamaño de ambas estructuras y luego verificarlo con sizeof.

Pregunta para responder en un comentario: Por qué SensorA y SensorB pueden tener tamaños distintos si contienen los mismos campos?


Ejercicio 14: Padding final en un arreglo

Enunciado: Calcular a mano el tamaño de una variable de tipo struct Punto y el tamaño de un arreglo de 5 puntos.

struct Punto {
    char etiqueta;
    float x;
    float y;
};

struct Punto puntos[5];

Responder:

  • Cuál es el offset de etiqueta?
  • Cuántos bytes de padding hay antes de x?
  • Cuál es el offset de x?
  • Cuál es el offset de y?
  • Cuánto vale sizeof(struct Punto)?
  • Cuánto vale sizeof(puntos)?

Ejercicio 15: Campo arreglo dentro de una estructura

Enunciado: Calcular el tamaño total de la siguiente estructura.

struct Producto {
    char codigo[7];
    int stock;
    char activo;
};

Completar:

CampoOffsetBytes del campoPadding antes
codigo
stock
activo

Luego responder:

  • Cuánto suman los campos sin padding?
  • Cuánto padding interno aparece?
  • Cuánto padding final aparece?
  • Cuánto vale sizeof(struct Producto)?

Ejercicio 16: Estructura anidada

Enunciado: Calcular primero el tamaño de struct Fecha y luego el tamaño de struct Ficha.

struct Fecha {
    int dia;
    int mes;
    int anio;
};

struct Ficha {
    char inicial;
    struct Fecha nacimiento;
    float promedio;
};

Responder:

  • Cuánto vale sizeof(struct Fecha)?
  • En qué offset empieza inicial dentro de struct Ficha?
  • En qué offset empieza nacimiento?
  • En qué offset empieza promedio?
  • Cuánto vale sizeof(struct Ficha)?

Ejercicio 17: Decidir si hay padding

Enunciado: Para cada estructura, indicar si tiene padding interno, padding final o ambos. Después calcular el tamaño total.

struct A {
    char a;
    char b;
    int c;
};

struct B {
    int a;
    float b;
    char c;
};

struct C {
    char a;
    int b;
    float c;
    char d;
};

Completar:

EstructuraPadding interno?Padding final?Tamaño total
struct A
struct B
struct C

Ejercicio 18: Reordenar para ocupar menos memoria

Enunciado: Calcular el tamaño de esta estructura en el orden dado.

struct Registro {
    char tipo;
    int ci;
    char sexo;
    float nota;
    char inicial;
};

Luego proponer otro orden para los mismos campos que ocupe menos memoria.

Responder:

  • Cuánto ocupa la estructura original?
  • Cuál sería un orden alternativo de los campos?
  • Cuánto ocupa la estructura con ese nuevo orden?
  • Cuántos bytes se ahorran por cada variable?
  • Si se declara struct Registro datos[100];, cuántos bytes se ahorrarían en total usando el orden alternativo?

Ejercicio 19: Encontrar el error en un cálculo

Enunciado: Un estudiante calculó el tamaño de esta estructura así:

struct Mensaje {
    char origen;
    int codigo;
    char destino;
};

Cálculo del estudiante:

origen: 1 byte
codigo: 4 bytes
destino: 1 byte
total: 6 bytes

Responder:

  • Qué parte del cálculo ignora?
  • Cuánto padding hay antes de codigo?
  • Cuánto padding final hay?
  • Cuál es el tamaño correcto?

Ejercicio 20: Comparar muchas estructuras

Enunciado: Sin compilar primero, ordenar estas estructuras de menor a mayor tamaño. Después verificar con sizeof.

struct X {
    char a;
    char b;
    char c;
    int d;
};

struct Y {
    char a;
    int b;
    char c;
    int d;
};

struct Z {
    int a;
    int b;
    char c;
    char d;
    char e;
};

Para cada una, escribir:

  • Tamaño de los campos sin padding.
  • Bytes de padding interno.
  • Bytes de padding final.
  • Tamaño total.

Recursos

Para Practicar

Bibliografía