Curso de C · Módulo 9 de 12

Structs, enum y typedef en C

Qué aprenderás en este módulo

modelar datos del mundo real agrupando valores.

Incluye 4 ejercicios con pistas y solución y un quiz de 5 preguntas. Tiempo estimado: 80 minutos.

1. struct: agrupar campos

struct Alumno {
    char nombre[32];
    int edad;
    double nota;
};

struct Alumno a = {"Ana", 20, 9.5};
a.edad++;                         // acceso con el punto
printf("%s %d\n", a.nombre, a.edad);
  • Se pasa por valor (copia entera) como cualquier variable. Para estructuras grandes pasa un puntero: void f(const struct Alumno *a).
  • Con puntero se accede con la flecha: p->edad ≡ (*p).edad.
  • Puedes tener arrays de structs: struct Alumno clase[30]; clase[2].nota = 7;
  • Y structs dentro de structs, y punteros dentro de structs.

2. typedef: ponerle nombre al tipo

typedef struct {
    double x, y;
} Punto;

Punto p = {1.5, 2.0};

Para estructuras que se refieren a sí mismas (listas, árboles) hay que dar nombre al struct:

typedef struct Nodo {
    int dato;
    struct Nodo *sig;     // aquí aún no existe el alias "Nodo"
} Nodo;

3. Inicialización cómoda (C99)

Punto p = { .y = 3.0, .x = 1.0 };          // por nombre de campo
Punto q = { 0 };                            // todo a cero

4. enum: constantes con nombre

enum Dia { LUN, MAR, MIE, JUE, VIE, SAB, DOM };   // LUN=0, MAR=1...
enum Dia hoy = MIE;
switch (hoy) { case SAB: case DOM: puts("finde"); break; default: puts("curro"); }

Puedes fijar valores: enum { OK = 0, ERR_ARCHIVO = 10, ERR_RED } (ERR_RED vale 11). Mucho más legible que "números mágicos".

5. union (breve)

Todos los campos comparten el mismo espacio de memoria; solo uno es válido a la vez. Se usa con una etiqueta que indica cuál:

typedef struct {
    enum { ENTERO, REAL } tipo;
    union { int i; double d; } v;
} Numero;

6. Tamaño y alineación

sizeof(struct) puede ser mayor que la suma de los campos: el compilador añade padding para alinear. Nunca asumas el tamaño; usa sizeof.

7. Ordenar con qsort

qsort está en <stdlib.h> y ordena cualquier array si le dices cómo comparar dos elementos:

int comparar(const void *pa, const void *pb) {
    const Alumno *a = pa, *b = pb;      // convierte void* al tipo real
    if (a->nota > b->nota) return -1;   // a antes que b
    if (a->nota < b->nota) return  1;
    return strcmp(a->nombre, b->nombre);
}
qsort(clase, n, sizeof clase[0], comparar);

Esto es pasar una función como parámetro (un puntero a función). Convención: devuelve <0 si a va antes, 0 si empatan, >0 si va después. (Nunca devuelvas a - b en enteros grandes: puede desbordar.)

Prueba esto

  1. Define struct Fecha {int d, m, a;} y una función que diga si una fecha es anterior a otra.
  2. Un array de 5 Punto y busca el más lejano al origen.
  3. Haz un enum Color {ROJO, VERDE, AZUL} y una función const char *nombre_color(enum Color).

Ejercicios prácticos del módulo 9

Programar se aprende programando: intenta cada ejercicio antes de mirar las pistas, y usa la solución solo para comparar. Marca los que resuelvas y tu progreso se guarda en este navegador.

9.1 Distancia entre puntos
Define typedef struct { double x, y; } Punto; y escribe double distancia(Punto a, Punto b) (teorema de Pitagoras, sqrt de <math.h>). El main lee dos puntos "x1 y1 x2 y2" e imprime la distancia con 3 decimales.

Entrada de ejemplo

0 0 3 4

Salida esperada

distancia=5.000

Tu punto de partida

#include <stdio.h>
#include <math.h>

typedef struct {
    double x, y;
} Punto;

double distancia(Punto a, Punto b) {
    // TODO
    return 0;
}

int main(void) {
    Punto p, q;
    scanf("%lf %lf %lf %lf", &p.x, &p.y, &q.x, &q.y);
    printf("distancia=%.3f\n", distancia(p, q));
    return 0;
}

Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.

Pista 1

Se accede a los campos con el punto: a.x, a.y.

Pista 2

distancia = sqrt(dx*dx + dy*dy).

Ver solución (inténtalo antes)
#include <stdio.h>
#include <math.h>

typedef struct {
    double x, y;
} Punto;

double distancia(Punto a, Punto b) {
    double dx = a.x - b.x, dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

int main(void) {
    Punto p, q;
    scanf("%lf %lf %lf %lf", &p.x, &p.y, &q.x, &q.y);
    printf("distancia=%.3f\n", distancia(p, q));
    return 0;
}
9.2 Lista de alumnos
Lee n y despues n lineas "nombre nota" (nombre sin espacios, nota real). Guardalos en un array de struct Alumno y muestra el mejor alumno y la nota media: mejor=Ana (9.50) media=7.83

Entrada de ejemplo

3
Ana 9.5
Luis 6
Mar 7.5

Salida esperada

mejor=Ana (9.50) media=7.67

Tu punto de partida

#include <stdio.h>

struct Alumno {
    char nombre[32];
    double nota;
};

int main(void) {
    struct Alumno a[50];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        // TODO: lee nombre (%31s) y nota (%lf) en a[i]
    }
    // TODO: busca el mejor, calcula la media e imprime

    return 0;
}

Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.

Pista 1

Un array de structs: a[i].nombre y a[i].nota.

Pista 2

Para el nombre (array de char) no hace falta &; para la nota (double) si.

Pista 3

Guarda el INDICE del mejor, no solo la nota: asi tienes tambien el nombre.

Ver solución (inténtalo antes)
#include <stdio.h>

struct Alumno {
    char nombre[32];
    double nota;
};

int main(void) {
    struct Alumno a[50];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%31s %lf", a[i].nombre, &a[i].nota);
    int mejor = 0;
    double suma = 0;
    for (int i = 0; i < n; i++) {
        if (a[i].nota > a[mejor].nota) mejor = i;
        suma += a[i].nota;
    }
    printf("mejor=%s (%.2f) media=%.2f\n", a[mejor].nombre, a[mejor].nota, suma / n);
    return 0;
}
9.3 Ordenar con qsort
Lee n alumnos igual que antes y muestralos ordenados por nota DESCENDENTE (en caso de empate, por nombre alfabetico). Usa qsort de <stdlib.h> con una funcion comparadora: int comparar(const void *a, const void *b) Salida: una linea por alumno: nombre nota(2 decimales)

Entrada de ejemplo

4
Luis 6
Ana 9.5
Mar 6
Eva 7

Salida esperada

Ana 9.50
Eva 7.00
Luis 6.00
Mar 6.00

Tu punto de partida

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char nombre[32];
    double nota;
} Alumno;

int comparar(const void *pa, const void *pb) {
    const Alumno *a = pa, *b = pb;
    // TODO: devuelve <0 si a va antes que b, >0 si va despues, 0 si empatan
    return 0;
}

int main(void) {
    Alumno v[50];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%31s %lf", v[i].nombre, &v[i].nota);
    qsort(v, n, sizeof v[0], comparar);
    for (int i = 0; i < n; i++) printf("%s %.2f\n", v[i].nombre, v[i].nota);
    return 0;
}

Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.

Pista 1

Con un puntero a struct se usa la flecha: a->nota (equivale a (*a).nota).

Pista 2

Para orden descendente, devuelve -1 cuando a->nota es MAYOR que b->nota.

Pista 3

Para el desempate, strcmp(a->nombre, b->nombre) ya devuelve <0, 0 o >0.

Ver solución (inténtalo antes)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char nombre[32];
    double nota;
} Alumno;

int comparar(const void *pa, const void *pb) {
    const Alumno *a = pa, *b = pb;
    if (a->nota > b->nota) return -1;
    if (a->nota < b->nota) return 1;
    return strcmp(a->nombre, b->nombre);
}

int main(void) {
    Alumno v[50];
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%31s %lf", v[i].nombre, &v[i].nota);
    qsort(v, n, sizeof v[0], comparar);
    for (int i = 0; i < n; i++) printf("%s %.2f\n", v[i].nombre, v[i].nota);
    return 0;
}
9.4 Semaforo con enum
Define enum Estado { VERDE, AMARILLO, ROJO }; e implementa enum Estado siguiente(enum Estado e) (VERDE->AMARILLO->ROJO->VERDE) const char *nombre(enum Estado e) ("VERDE", "AMARILLO", "ROJO") usando switch. El main parte de ROJO y muestra los n estados siguientes, uno por linea.

Entrada de ejemplo

4

Salida esperada

VERDE
AMARILLO
ROJO
VERDE

Tu punto de partida

#include <stdio.h>

enum Estado { VERDE, AMARILLO, ROJO };

enum Estado siguiente(enum Estado e) {
    // TODO
    return e;
}

const char *nombre(enum Estado e) {
    // TODO
    return "?";
}

int main(void) {
    int n;
    scanf("%d", &n);
    enum Estado e = ROJO;
    for (int i = 0; i < n; i++) {
        e = siguiente(e);
        printf("%s\n", nombre(e));
    }
    return 0;
}

Cópialo en tu editor, complétalo, compílalo (gcc -Wall -Wextra programa.c -o programa) y prueba la entrada de ejemplo. Para que se corrija solo con varios casos de prueba, descarga el curso.

Pista 1

En un switch sobre un enum puedes devolver directamente en cada case; no hace falta break despues de return.

Pista 2

Devolver un literal de cadena desde una funcion es seguro: vive durante todo el programa.

Ver solución (inténtalo antes)
#include <stdio.h>

enum Estado { VERDE, AMARILLO, ROJO };

enum Estado siguiente(enum Estado e) {
    switch (e) {
        case VERDE: return AMARILLO;
        case AMARILLO: return ROJO;
        default: return VERDE;
    }
}

const char *nombre(enum Estado e) {
    switch (e) {
        case VERDE: return "VERDE";
        case AMARILLO: return "AMARILLO";
        default: return "ROJO";
    }
}

int main(void) {
    int n;
    scanf("%d", &n);
    enum Estado e = ROJO;
    for (int i = 0; i < n; i++) {
        e = siguiente(e);
        printf("%s\n", nombre(e));
    }
    return 0;
}

Quiz del módulo 9

5 preguntas para comprobar que has fijado los conceptos. Las opciones se barajan cada vez.