11 - Estructuras (`struct`)
Objetivos
Agrupar datos relacionados en C usando estructuras para modelar entidades con varios campos.
- Comprender qué problema resuelve
structfrente a variables sueltas o arreglos paralelos - Definir tipos de estructura con campos de distinto tipo
- Declarar e inicializar variables de estructura
- Acceder correctamente a los campos con el operador
. - Trabajar con arreglos de estructuras y recorridos con
for - Usar estructuras como parámetros y valores de retorno en funciones simples
Contenidos
Qué problema resuelven las estructuras
- Motivación: una misma entidad puede necesitar varios datos relacionados, por ejemplo nombre, edad y promedio de un alumno
- Límite de otras alternativas: usar variables separadas o arreglos paralelos vuelve el código más difícil de mantener
- Idea central: una estructura reúne esos datos bajo un solo nombre
Definición de struct
- Forma general: declarar un nuevo tipo con varios campos internos
- Flexibilidad: cada campo puede tener un tipo distinto
- Importancia conceptual: definir la estructura describe el tipo, pero todavía no crea una variable concreta
Declaración, inicialización y acceso a campos
- Declaración: crear variables concretas de tipo
struct - Inicialización: cargar datos campo a campo o con llaves respetando el orden de definición
- Acceso: usar
variable.campopara leer o modificar cada parte de la estructura
Campos compuestos y arreglos de estructuras
- Campos de tipo arreglo: una estructura puede incluir cadenas como
char nombre[20] - Arreglo de estructuras: permite manejar varias entidades del mismo tipo con un único nombre base
- Patrón típico: combinar índice y campo, por ejemplo
grupo[i].promedio
Estructuras y funciones
- Parámetros: una función puede recibir una estructura completa
- Retorno: también puede devolver una estructura como resultado
- Relación con lo ya visto: sigue aplicando el paso por valor trabajado en funciones
Errores frecuentes y cierre
- Campo fuera de contexto: olvidar la variable y escribir solo el nombre del campo
- Confusión de niveles: mezclar arreglo de estructuras con una estructura individual
- Extensión opcional: uso básico de
typedefcomo mejora de escritura, sin cambiar la idea principal
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:
- Cargar 5 alumnos.
- Mostrar todos los alumnos.
- Actualizar el promedio del primer alumno.
- Mostrar el promedio general del grupo.
- Mostrar cuántos alumnos aprobaron.
- 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:
charocupa 1 byte y alinea a 1.intocupa 4 bytes y alinea a 4.floatocupa 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:
| Campo | Offset | Bytes del campo | Padding 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
inicialdentro destruct 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:
| Estructura | Padding 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
- “El lenguaje de programación C” - Kernighan & Ritchie (Capítulo 6)
- “Cómo programar en C/C++” - Deitel & Deitel