2. Apuntadores y asignación de memoria

#estructuraDeDatos #universidad

a. Apuntadores

Apuntadores son variables que almacenan la dirección de memoria de otra variable. Son fundamentales en la gestión dinámica de memoria y en la implementación de estructuras de datos complejas.

Características de los apuntadores:

Ejemplo:

int a = 10;
int *p = &a; // p almacena la dirección de memoria de 'a'

printf("Valor de a: %d\n", a);      // Imprime 10
printf("Valor de *p: %d\n", *p);    // Imprime 10
printf("Dirección de a: %p\n", (void*)&a); // Imprime la dirección de 'a'
printf("Valor de p: %p\n", (void*)p);       // Imprime la dirección de 'a'

b. Asignación dinámica de memoria

La asignación dinámica de memoria permite reservar y liberar memoria durante la ejecución del programa según las necesidades.

Funciones principales en C:

Ejemplo:

int *arr;
int n = 5;

// Asignación de memoria para 5 enteros
arr = (int*)malloc(n * sizeof(int));

if(arr == NULL) {
    printf("Error en la asignación de memoria.\n");
    exit(1);
}

// Asignación de valores
for(int i = 0; i < n; i++) {
    arr[i] = i + 1;
}

// Liberar memoria
free(arr);

c. Estructuras

Estructuras son tipos de datos compuestos que permiten agrupar variables de diferentes tipos bajo un mismo nombre.

Características:

Ejemplo:

typedef struct {
    char nombre[50];
    int edad;
    float salario;
} Empleado;

Empleado emp1;

// Asignación de valores
strcpy(emp1.nombre, "Juan Pérez");
emp1.edad = 30;
emp1.salario = 45000.50;

Ejemplos Prácticos

Ejemplo 3: Uso de apuntadores para intercambiar valores

void intercambiar(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 5, y = 10;
    printf("Antes: x = %d, y = %d\n", x, y);
    intercambiar(&x, &y);
    printf("Después: x = %d, y = %d\n", x, y);
    return 0;
}

Salida:

antes: x = 5, y = 10
Despues: x = 10, y = 5

Ejemplo 4: Implementación dinámica de una estructura de datos (Lista enlazada simple)

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

typedef struct Nodo {
    int dato;
    struct Nodo *siguiente;
} Nodo;

// Función para crear un nuevo nodo
Nodo* crearNodo(int valor) {
    Nodo *nuevo = (Nodo*)malloc(sizeof(Nodo));
    if (!nuevo) {
        printf("Error en la asignación de memoria.\n");
        exit(1);
    }
    nuevo->dato = valor;
    nuevo->siguiente = NULL;
    return nuevo;
}

// Función para imprimir la lista
void imprimirLista(Nodo *cabeza) {
    Nodo *actual = cabeza;
    while(actual != NULL) {
        printf("%d -> ", actual->dato);
        actual = actual->siguiente;
    }
    printf("NULL\n");
}

int main() {
    Nodo *cabeza = crearNodo(1);
    cabeza->siguiente = crearNodo(2);
    cabeza->siguiente->siguiente = crearNodo(3);

    imprimirLista(cabeza);

    // Liberar memoria
    Nodo *actual = cabeza;
    Nodo *temp;
    while(actual != NULL) {
        temp = actual;
        actual = actual->siguiente;
        free(temp);
    }

    return 0;
}

Salida:

1 -> 2 -> 3 -> NULL

Ejercicios

  1. Manipulación de apuntadores:

    • Escribe una función en C que reciba un arreglo de enteros y su tamaño, y que invierta el arreglo utilizando apuntadores.
  2. Asignación dinámica:

    • Implementa una función que lea n números enteros desde el usuario, almacénalos en un arreglo dinámico y luego calcule y retorne el promedio de los números.
  3. Uso de estructuras y memoria dinámica:

    • Define una estructura Libro con los campos titulo, autor y precio. Luego, crea una función que permita ingresar datos para m libros utilizando memoria dinámica y luego los muestre por pantalla.