C

C

Tutorial C: Creación de Structs

Aprende a inicializar, asignar y pasar estructuras en C con ejemplos prácticos para mejorar tu programación en C.

Aprende C y certifícate

Inicialización de estructuras

La inicialización de estructuras en C es un proceso fundamental que nos permite asignar valores iniciales a los miembros de una estructura cuando la declaramos. Una estructura (o struct) es un tipo de dato compuesto que agrupa variables de diferentes tipos bajo un mismo nombre, lo que nos permite organizar datos relacionados de manera lógica.

Existen varias formas de inicializar una estructura en C, cada una con sus propias características y casos de uso. Vamos a explorar estas opciones utilizando ejemplos prácticos.

Declaración sin inicialización

Antes de ver cómo inicializar estructuras, recordemos cómo se declara una estructura básica:

struct Libro {
    char titulo[50];
    char autor[50];
    int anio;
    float precio;
};

Cuando declaramos una variable de tipo estructura sin inicializarla, sus miembros contendrán valores indeterminados (basura):

struct Libro miLibro;  // Los miembros contienen valores aleatorios

Inicialización por miembros

La forma más básica de inicializar una estructura es asignando valores a cada miembro individualmente después de declarar la variable:

struct Libro miLibro;
strcpy(miLibro.titulo, "El Quijote");
strcpy(miLibro.autor, "Miguel de Cervantes");
miLibro.anio = 1605;
miLibro.precio = 15.99;

Esta técnica es útil cuando necesitamos asignar valores en diferentes partes del código o cuando los valores provienen de cálculos o entradas del usuario.

Inicialización en la declaración

C nos permite inicializar una estructura directamente en su declaración utilizando una lista de valores entre llaves {}. Los valores se asignan a los miembros en el mismo orden en que están declarados:

struct Libro miLibro = {"El Quijote", "Miguel de Cervantes", 1605, 15.99};

Esta sintaxis es concisa y clara cuando conocemos todos los valores de antemano.

Inicialización con designadores

A partir del estándar C99, podemos usar designadores para inicializar miembros específicos en cualquier orden:

struct Libro miLibro = {
    .autor = "Miguel de Cervantes",
    .titulo = "El Quijote",
    .precio = 15.99,
    .anio = 1605
};

Esta técnica ofrece varias ventajas:

  • Podemos inicializar los miembros en cualquier orden
  • Mejora la legibilidad del código
  • Si omitimos algún miembro, se inicializará automáticamente a cero

Inicialización parcial

No es necesario inicializar todos los miembros de una estructura. Los miembros no especificados se inicializan automáticamente a cero:

struct Libro miLibro = {.titulo = "El Quijote", .autor = "Miguel de Cervantes"};
// anio y precio se inicializan a 0

Inicialización de estructuras anidadas

Cuando tenemos estructuras dentro de otras estructuras, podemos inicializarlas de forma anidada:

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

struct LibroDetallado {
    char titulo[50];
    char autor[50];
    struct Fecha fechaPublicacion;
    float precio;
};

struct LibroDetallado miLibro = {
    "El Quijote",
    "Miguel de Cervantes",
    {5, 1, 1605},  // Inicialización de la estructura anidada Fecha
    15.99
};

Con designadores, la inicialización de estructuras anidadas es aún más clara:

struct LibroDetallado miLibro = {
    .titulo = "El Quijote",
    .autor = "Miguel de Cervantes",
    .fechaPublicacion = {.dia = 5, .mes = 1, .anio = 1605},
    .precio = 15.99
};

Inicialización de arrays de estructuras

Podemos inicializar arrays de estructuras de manera similar a como inicializamos arrays de tipos básicos:

struct Libro biblioteca[3] = {
    {"El Quijote", "Miguel de Cervantes", 1605, 15.99},
    {"Cien años de soledad", "Gabriel García Márquez", 1967, 12.50},
    {"1984", "George Orwell", 1949, 10.75}
};

Con designadores:

struct Libro biblioteca[3] = {
    [0] = {.titulo = "El Quijote", .autor = "Miguel de Cervantes", 
           .anio = 1605, .precio = 15.99},
    [1] = {.titulo = "Cien años de soledad", .autor = "Gabriel García Márquez", 
           .anio = 1967, .precio = 12.50},
    [2] = {.titulo = "1984", .autor = "George Orwell", 
           .anio = 1949, .precio = 10.75}
};

Inicialización con typedef

Cuando usamos typedef para crear un alias para nuestras estructuras, la inicialización funciona exactamente igual, pero sin necesidad de usar la palabra struct:

typedef struct {
    char titulo[50];
    char autor[50];
    int anio;
    float precio;
} Libro;

Libro miLibro = {"El Quijote", "Miguel de Cervantes", 1605, 15.99};

Ejemplo práctico completo

Veamos un ejemplo completo que muestra diferentes formas de inicializar una estructura Libro:

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

// Definición de la estructura
struct Libro {
    char titulo[50];
    char autor[50];
    int anio;
    float precio;
};

int main() {
    // Método 1: Inicialización por miembros
    struct Libro libro1;
    strcpy(libro1.titulo, "El Principito");
    strcpy(libro1.autor, "Antoine de Saint-Exupéry");
    libro1.anio = 1943;
    libro1.precio = 9.99;
    
    // Método 2: Inicialización en la declaración
    struct Libro libro2 = {"Fahrenheit 451", "Ray Bradbury", 1953, 11.25};
    
    // Método 3: Inicialización con designadores (C99)
    struct Libro libro3 = {
        .titulo = "El Hobbit",
        .autor = "J.R.R. Tolkien",
        .anio = 1937,
        .precio = 14.50
    };
    
    // Mostrar los libros
    printf("Libro 1: %s por %s (%d) - %.2f€\n", 
           libro1.titulo, libro1.autor, libro1.anio, libro1.precio);
    
    printf("Libro 2: %s por %s (%d) - %.2f€\n", 
           libro2.titulo, libro2.autor, libro2.anio, libro2.precio);
    
    printf("Libro 3: %s por %s (%d) - %.2f€\n", 
           libro3.titulo, libro3.autor, libro3.anio, libro3.precio);
    
    return 0;
}

Este programa muestra tres formas diferentes de inicializar estructuras y luego imprime la información de cada libro.

Consideraciones importantes

  • La inicialización con designadores (disponible desde C99) es generalmente la opción más recomendada por su claridad y flexibilidad.
  • Si estás trabajando con cadenas de caracteres dentro de estructuras, recuerda que debes usar funciones como strcpy() para asignar valores después de la declaración, a menos que inicialices la estructura en la misma declaración.
  • Al inicializar estructuras grandes, considera usar la inicialización por miembros para mejorar la legibilidad del código.
  • Si no inicializas explícitamente una estructura global o estática, todos sus miembros se inicializan automáticamente a cero.

La elección del método de inicialización dependerá del contexto específico de tu programa y de tus preferencias personales en cuanto a estilo de código.

Asignación entre estructuras

Una vez que hemos creado e inicializado nuestras estructuras, a menudo necesitamos copiar los datos de una estructura a otra. En C, podemos asignar estructuras completas de manera directa, lo que facilita enormemente el manejo de datos complejos.

La asignación entre estructuras nos permite copiar todos los miembros de una estructura a otra del mismo tipo en una sola operación. Esto es una ventaja significativa frente a otros lenguajes que requieren copiar cada miembro individualmente.

Asignación básica entre estructuras

Para asignar una estructura a otra, simplemente usamos el operador de asignación (=):

struct Libro libro1 = {"Don Quijote", "Miguel de Cervantes", 1605, 19.99};
struct Libro libro2;

// Asignación completa de libro1 a libro2
libro2 = libro1;

Con esta simple instrucción, todos los valores de los miembros de libro1 se copian a los miembros correspondientes de libro2. Ahora ambas estructuras contienen la misma información, pero son entidades independientes en memoria.

Comportamiento de la asignación

Es importante entender qué ocurre exactamente durante la asignación:

#include <stdio.h>

struct Punto {
    int x;
    int y;
};

int main() {
    struct Punto p1 = {10, 20};
    struct Punto p2;
    
    p2 = p1;  // Copia todos los miembros de p1 a p2
    
    // Modificamos p1 después de la asignación
    p1.x = 30;
    
    // Mostramos ambos puntos
    printf("p1: (%d, %d)\n", p1.x, p1.y);  // Muestra: p1: (30, 20)
    printf("p2: (%d, %d)\n", p2.x, p2.y);  // Muestra: p2: (10, 20)
    
    return 0;
}

Como puedes ver, después de la asignación, modificar p1 no afecta a p2. Esto demuestra que la asignación realiza una copia por valor de todos los miembros, creando dos estructuras independientes.

Asignación con arrays dentro de estructuras

Cuando una estructura contiene arrays, la asignación también copia todos los elementos del array:

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

struct Estudiante {
    char nombre[50];
    int edad;
    float calificaciones[3];
};

int main() {
    struct Estudiante est1 = {"Ana García", 20, {9.5, 8.7, 9.2}};
    struct Estudiante est2;
    
    est2 = est1;  // Copia todos los miembros, incluyendo el array completo
    
    // Modificamos un elemento del array en est1
    est1.calificaciones[0] = 7.8;
    
    // Mostramos las calificaciones de ambos estudiantes
    printf("Calificaciones de %s: %.1f, %.1f, %.1f\n", 
           est1.nombre, est1.calificaciones[0], est1.calificaciones[1], est1.calificaciones[2]);
    
    printf("Calificaciones de %s: %.1f, %.1f, %.1f\n", 
           est2.nombre, est2.calificaciones[0], est2.calificaciones[1], est2.calificaciones[2]);
    
    return 0;
}

La salida mostrará que cambiar el array en est1 no afecta al array en est2, confirmando que se realizó una copia completa.

Limitaciones de la asignación

Aunque la asignación de estructuras es muy conveniente, tiene algunas limitaciones importantes:

  • Solo funciona entre estructuras del mismo tipo: No puedes asignar una estructura de un tipo a una estructura de otro tipo, incluso si tienen miembros idénticos.
struct Punto2D {
    int x;
    int y;
};

struct Coordenada {
    int x;
    int y;
};

int main() {
    struct Punto2D p = {5, 10};
    struct Coordenada c;
    
    // c = p;  // Error: tipos incompatibles
    
    // Solución: asignar miembro por miembro
    c.x = p.x;
    c.y = p.y;
    
    return 0;
}
  • Cuidado con los punteros: Si una estructura contiene punteros, la asignación solo copia la dirección del puntero, no los datos a los que apunta (copia superficial).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Persona {
    char *nombre;
    int edad;
};

int main() {
    struct Persona p1;
    struct Persona p2;
    
    // Asignamos memoria para el nombre de p1
    p1.nombre = (char*)malloc(20 * sizeof(char));
    strcpy(p1.nombre, "Carlos");
    p1.edad = 25;
    
    // Asignamos p1 a p2
    p2 = p1;
    
    // Modificamos el nombre a través de p2
    strcpy(p2.nombre, "Roberto");
    
    // Ambos nombres han cambiado porque comparten el mismo puntero
    printf("Nombre p1: %s\n", p1.nombre);  // Muestra: Roberto
    printf("Nombre p2: %s\n", p2.nombre);  // Muestra: Roberto
    
    // Liberamos memoria (solo una vez, ya que ambos apuntan al mismo lugar)
    free(p1.nombre);
    // No debemos hacer free(p2.nombre) aquí
    
    return 0;
}

Asignación parcial de miembros

A veces solo necesitamos copiar algunos miembros específicos de una estructura a otra:

struct Empleado {
    char nombre[50];
    int id;
    float salario;
    int departamento;
};

int main() {
    struct Empleado emp1 = {"Juan Pérez", 1001, 2500.0, 3};
    struct Empleado emp2 = {"María López", 1002, 2300.0, 2};
    
    // Copiamos solo el salario y departamento
    emp2.salario = emp1.salario;
    emp2.departamento = emp1.departamento;
    
    return 0;
}

Asignación en arrays de estructuras

La asignación también funciona con elementos individuales de un array de estructuras:

#include <stdio.h>

struct Producto {
    char nombre[50];
    float precio;
    int stock;
};

int main() {
    struct Producto inventario[3] = {
        {"Teclado", 25.99, 10},
        {"Ratón", 15.50, 20},
        {"Monitor", 199.99, 5}
    };
    
    struct Producto productoDestacado;
    
    // Copiamos el segundo producto al producto destacado
    productoDestacado = inventario[1];
    
    printf("Producto destacado: %s - %.2f€ (Stock: %d)\n", 
           productoDestacado.nombre, productoDestacado.precio, productoDestacado.stock);
    
    return 0;
}

Ejemplo práctico: Gestión de libros

Veamos un ejemplo más completo que muestra cómo la asignación de estructuras puede ser útil en una aplicación real:

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

struct Libro {
    char titulo[100];
    char autor[50];
    int anio;
    float precio;
    int disponible;
};

void mostrarLibro(struct Libro libro) {
    printf("Título: %s\n", libro.titulo);
    printf("Autor: %s\n", libro.autor);
    printf("Año: %d\n", libro.anio);
    printf("Precio: %.2f€\n", libro.precio);
    printf("Disponible: %s\n", libro.disponible ? "Sí" : "No");
    printf("------------------------\n");
}

int main() {
    // Biblioteca con 3 libros
    struct Libro biblioteca[3] = {
        {"El principito", "Antoine de Saint-Exupéry", 1943, 12.99, 1},
        {"1984", "George Orwell", 1949, 10.50, 0},
        {"Cien años de soledad", "Gabriel García Márquez", 1967, 15.75, 1}
    };
    
    // Libro para el préstamo
    struct Libro libroPrestado;
    
    // Simulamos un préstamo copiando el libro
    libroPrestado = biblioteca[0];
    
    // Marcamos el libro original como no disponible
    biblioteca[0].disponible = 0;
    
    printf("Libro original después del préstamo:\n");
    mostrarLibro(biblioteca[0]);
    
    printf("Libro prestado:\n");
    mostrarLibro(libroPrestado);
    
    // Cuando se devuelve el libro, actualizamos su estado
    biblioteca[0].disponible = 1;
    
    printf("Libro original después de la devolución:\n");
    mostrarLibro(biblioteca[0]);
    
    return 0;
}

Este ejemplo muestra cómo podemos usar la asignación de estructuras para crear una copia de un libro cuando se presta, manteniendo la información original en la biblioteca pero actualizando su estado de disponibilidad.

Buenas prácticas

  • Verifica la compatibilidad de tipos: Asegúrate de que las estructuras sean del mismo tipo antes de realizar una asignación.
  • Ten cuidado con los punteros: Si tu estructura contiene punteros, considera implementar una función de "copia profunda" que duplique también los datos apuntados.
  • Considera el uso de funciones: Para operaciones complejas o repetitivas, crea funciones específicas para copiar estructuras.
  • Evita asignaciones innecesarias: Las estructuras grandes pueden consumir mucha memoria, así que evita copias innecesarias.

La asignación entre estructuras es una característica poderosa de C que simplifica enormemente el manejo de datos complejos, permitiéndonos trabajar con estructuras de manera eficiente y clara.

Estructuras como parámetros

Cuando trabajamos con estructuras en C, a menudo necesitamos procesar estos datos en diferentes partes de nuestro programa. Para esto, es fundamental saber cómo pasar estructuras a funciones. Existen dos formas principales de hacerlo: por valor y por referencia, cada una con sus propias características y casos de uso.

Paso de estructuras por valor

Cuando pasamos una estructura por valor, la función recibe una copia completa de la estructura original. Esto significa que cualquier modificación realizada dentro de la función no afectará a la estructura original.

#include <stdio.h>

struct Punto {
    int x;
    int y;
};

// Función que recibe una estructura por valor
void mostrarPunto(struct Punto p) {
    printf("Coordenadas: (%d, %d)\n", p.x, p.y);
}

// Función que intenta modificar la estructura
void desplazarPunto(struct Punto p) {
    p.x += 5;
    p.y += 5;
    printf("Dentro de la función: (%d, %d)\n", p.x, p.y);
}

int main() {
    struct Punto miPunto = {10, 20};
    
    mostrarPunto(miPunto);
    
    desplazarPunto(miPunto);
    
    // La estructura original no se modifica
    printf("Después de llamar a la función: (%d, %d)\n", miPunto.x, miPunto.y);
    
    return 0;
}

En este ejemplo, desplazarPunto modifica una copia local de la estructura, pero la estructura original miPunto en main() permanece sin cambios.

Ventajas del paso por valor:

  • Seguridad: La estructura original no puede ser modificada accidentalmente.
  • Simplicidad: El código es más fácil de entender y depurar.

Desventajas del paso por valor:

  • Consumo de memoria: Se crea una copia completa de la estructura, lo que puede ser ineficiente para estructuras grandes.
  • Rendimiento: Copiar estructuras grandes puede afectar el rendimiento del programa.

Paso de estructuras por referencia

Para modificar una estructura dentro de una función o para evitar la sobrecarga de copiar estructuras grandes, podemos pasar estructuras por referencia utilizando punteros.

#include <stdio.h>

struct Rectangulo {
    int ancho;
    int alto;
    int area;
};

// Función que recibe un puntero a una estructura
void calcularArea(struct Rectangulo *r) {
    r->area = r->ancho * r->alto;
}

int main() {
    struct Rectangulo miRect = {5, 10, 0};
    
    printf("Antes: ancho=%d, alto=%d, area=%d\n", 
           miRect.ancho, miRect.alto, miRect.area);
    
    calcularArea(&miRect);
    
    printf("Después: ancho=%d, alto=%d, area=%d\n", 
           miRect.ancho, miRect.alto, miRect.area);
    
    return 0;
}

En este ejemplo, calcularArea recibe un puntero a la estructura Rectangulo y modifica directamente el miembro area de la estructura original.

Sintaxis para acceder a miembros a través de punteros:

Cuando trabajamos con punteros a estructuras, usamos el operador -> para acceder a los miembros:

struct Persona {
    char nombre[50];
    int edad;
};

void cumplirAnios(struct Persona *p) {
    p->edad++;  // Equivalente a (*p).edad++
}

Ventajas del paso por referencia:

  • Eficiencia: No se crea una copia de la estructura, lo que ahorra memoria.
  • Modificación: Permite modificar la estructura original.
  • Rendimiento: Es más rápido para estructuras grandes.

Desventajas del paso por referencia:

  • Complejidad: El código puede ser más difícil de seguir.
  • Riesgo: Posibilidad de modificar accidentalmente la estructura original.

Ejemplo práctico: Gestión de libros

Veamos un ejemplo más completo que utiliza ambos enfoques:

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

struct Libro {
    char titulo[100];
    char autor[50];
    int anio;
    float precio;
};

// Función que recibe un libro por valor (solo lectura)
void mostrarLibro(struct Libro libro) {
    printf("Título: %s\n", libro.titulo);
    printf("Autor: %s\n", libro.autor);
    printf("Año: %d\n", libro.anio);
    printf("Precio: %.2f€\n", libro.precio);
    printf("------------------------\n");
}

// Función que recibe un libro por referencia (para modificarlo)
void aplicarDescuento(struct Libro *libro, float porcentaje) {
    float descuento = libro->precio * (porcentaje / 100.0);
    libro->precio -= descuento;
}

int main() {
    struct Libro miLibro = {"El Hobbit", "J.R.R. Tolkien", 1937, 25.99};
    
    printf("Información del libro:\n");
    mostrarLibro(miLibro);
    
    printf("Aplicando descuento del 20%%...\n");
    aplicarDescuento(&miLibro, 20.0);
    
    printf("Información actualizada:\n");
    mostrarLibro(miLibro);
    
    return 0;
}

En este ejemplo:

  • mostrarLibro recibe la estructura por valor porque solo necesita leer sus datos.
  • aplicarDescuento recibe un puntero a la estructura porque necesita modificar el precio.

Arrays de estructuras como parámetros

Cuando trabajamos con arrays de estructuras, generalmente los pasamos por referencia:

#include <stdio.h>

struct Estudiante {
    char nombre[50];
    float calificacion;
};

// Función que recibe un array de estructuras
void mostrarEstudiantes(struct Estudiante *lista, int cantidad) {
    for (int i = 0; i < cantidad; i++) {
        printf("Estudiante %d: %s - Calificación: %.1f\n", 
               i+1, lista[i].nombre, lista[i].calificacion);
    }
}

// Función para calcular el promedio de calificaciones
float calcularPromedio(struct Estudiante *lista, int cantidad) {
    float suma = 0;
    for (int i = 0; i < cantidad; i++) {
        suma += lista[i].calificacion;
    }
    return suma / cantidad;
}

int main() {
    struct Estudiante clase[3] = {
        {"Ana García", 8.5},
        {"Pedro López", 7.2},
        {"María Rodríguez", 9.3}
    };
    
    mostrarEstudiantes(clase, 3);
    
    float promedio = calcularPromedio(clase, 3);
    printf("Promedio de la clase: %.2f\n", promedio);
    
    return 0;
}

Observa que cuando pasamos un array de estructuras, no necesitamos usar el operador & porque el nombre del array ya es un puntero al primer elemento.

Estructuras que contienen punteros

Cuando una estructura contiene punteros, debemos tener especial cuidado al pasarla como parámetro:

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

struct Persona {
    char *nombre;
    int edad;
};

// Inicializa una persona asignando memoria para el nombre
void inicializarPersona(struct Persona *p, const char *nombre, int edad) {
    p->nombre = (char*)malloc(strlen(nombre) + 1);
    strcpy(p->nombre, nombre);
    p->edad = edad;
}

// Libera la memoria asignada para el nombre
void liberarPersona(struct Persona *p) {
    free(p->nombre);
    p->nombre = NULL;
}

int main() {
    struct Persona alguien;
    
    inicializarPersona(&alguien, "Carlos Gómez", 30);
    
    printf("Nombre: %s, Edad: %d\n", alguien.nombre, alguien.edad);
    
    liberarPersona(&alguien);
    
    return 0;
}

En este caso, es crucial pasar la estructura por referencia para gestionar correctamente la memoria dinámica.

Retorno de estructuras desde funciones

Además de pasar estructuras como parámetros, también podemos retornarlas desde funciones:

#include <stdio.h>

struct Punto {
    int x;
    int y;
};

// Función que retorna una estructura
struct Punto crearPunto(int x, int y) {
    struct Punto nuevo;
    nuevo.x = x;
    nuevo.y = y;
    return nuevo;
}

// Función que retorna una estructura calculada
struct Punto puntoMedio(struct Punto p1, struct Punto p2) {
    struct Punto medio;
    medio.x = (p1.x + p2.x) / 2;
    medio.y = (p1.y + p2.y) / 2;
    return medio;
}

int main() {
    struct Punto a = crearPunto(10, 20);
    struct Punto b = crearPunto(30, 40);
    
    printf("Punto A: (%d, %d)\n", a.x, a.y);
    printf("Punto B: (%d, %d)\n", b.x, b.y);
    
    struct Punto medio = puntoMedio(a, b);
    printf("Punto medio: (%d, %d)\n", medio.x, medio.y);
    
    return 0;
}

Al retornar estructuras, C crea una copia de la estructura local y la devuelve al llamador. Esto puede ser ineficiente para estructuras grandes.

Cuándo usar cada enfoque

  • Paso por valor (copia):

  • Para estructuras pequeñas (pocos bytes)

  • Cuando solo necesitas leer los datos

  • Cuando quieres garantizar que la estructura original no se modifique

  • Paso por referencia (puntero):

  • Para estructuras grandes (ahorra memoria)

  • Cuando necesitas modificar la estructura original

  • Para mejorar el rendimiento

  • Cuando la estructura contiene punteros que requieren gestión de memoria

Ejemplo completo: Sistema de biblioteca

Veamos un ejemplo más completo que combina varios conceptos:

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

struct Libro {
    char titulo[100];
    char autor[50];
    int anio;
    float precio;
    int disponible;
};

// Muestra información de un libro (paso por valor)
void mostrarLibro(struct Libro libro) {
    printf("Título: %s\n", libro.titulo);
    printf("Autor: %s\n", libro.autor);
    printf("Año: %d\n", libro.anio);
    printf("Precio: %.2f€\n", libro.precio);
    printf("Estado: %s\n", libro.disponible ? "Disponible" : "Prestado");
    printf("------------------------\n");
}

// Presta un libro (paso por referencia)
int prestarLibro(struct Libro *libro) {
    if (libro->disponible) {
        libro->disponible = 0;
        return 1;  // Préstamo exitoso
    }
    return 0;  // Libro no disponible
}

// Devuelve un libro (paso por referencia)
void devolverLibro(struct Libro *libro) {
    libro->disponible = 1;
}

// Busca un libro por título en un array de libros
struct Libro* buscarLibro(struct Libro *biblioteca, int numLibros, const char *titulo) {
    for (int i = 0; i < numLibros; i++) {
        if (strcmp(biblioteca[i].titulo, titulo) == 0) {
            return &biblioteca[i];
        }
    }
    return NULL;  // Libro no encontrado
}

int main() {
    struct Libro biblioteca[3] = {
        {"El principito", "Antoine de Saint-Exupéry", 1943, 12.99, 1},
        {"1984", "George Orwell", 1949, 10.50, 1},
        {"Cien años de soledad", "Gabriel García Márquez", 1967, 15.75, 1}
    };
    
    // Mostrar todos los libros
    printf("Catálogo de la biblioteca:\n");
    for (int i = 0; i < 3; i++) {
        mostrarLibro(biblioteca[i]);
    }
    
    // Buscar y prestar un libro
    struct Libro *libro = buscarLibro(biblioteca, 3, "1984");
    if (libro != NULL) {
        printf("Libro encontrado. Intentando prestar...\n");
        if (prestarLibro(libro)) {
            printf("Libro prestado con éxito.\n");
            mostrarLibro(*libro);
        } else {
            printf("El libro no está disponible para préstamo.\n");
        }
    } else {
        printf("Libro no encontrado en la biblioteca.\n");
    }
    
    // Devolver el libro
    printf("Devolviendo el libro...\n");
    devolverLibro(libro);
    mostrarLibro(*libro);
    
    return 0;
}

Este ejemplo muestra:

  • Paso por valor para mostrar información (mostrarLibro)
  • Paso por referencia para modificar estructuras (prestarLibro, devolverLibro)
  • Retorno de punteros a estructuras (buscarLibro)
  • Manipulación de arrays de estructuras

Consideraciones finales

  • Consistencia: Decide qué enfoque usar para cada función según sus necesidades y sé consistente.
  • Documentación: Documenta claramente si una función modifica o no la estructura original.
  • Eficiencia: Para estructuras muy grandes, considera siempre el paso por referencia.
  • Claridad: Usa nombres de funciones que indiquen si modifican la estructura (como calcularArea vs obtenerArea).

El manejo adecuado de estructuras como parámetros es fundamental para crear programas en C que sean eficientes, claros y fáciles de mantener.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

30 % DE DESCUENTO

Plan mensual

19.00 /mes

13.30 € /mes

Precio normal mensual: 19 €
63 % DE DESCUENTO

Plan anual

10.00 /mes

7.00 € /mes

Ahorras 144 € al año
Precio normal anual: 120 €
Aprende C online

Todas las lecciones de C

Accede a todas las lecciones de C y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a C y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender las diferentes formas de inicializar estructuras en C, incluyendo inicialización por miembros, en la declaración y con designadores.
  • Aprender a asignar estructuras completas y entender las limitaciones y comportamientos de la asignación.
  • Conocer cómo pasar estructuras a funciones por valor y por referencia, y cuándo usar cada método.
  • Manejar estructuras anidadas y arrays de estructuras, así como estructuras que contienen punteros.
  • Aplicar buenas prácticas para el uso eficiente y seguro de estructuras en programas en C.