9 - Errores comunes en C al trabajar con listas enlazadas

Errores que conviene detectar a tiempo

Las listas enlazadas combinan punteros, memoria dinámica y lógica de enlaces. Esta mezcla genera errores frecuentes que pueden causar fallas difíciles de depurar. A continuación se describen los más habituales y cómo prevenirlos.

9.1 Punteros no inicializados

Un puntero no inicializado contiene basura y puede apuntar a direcciones aleatorias. Esto causa comportamiento indefinido al primer acceso.

Nodo *cabeza; /* ERROR: valor indefinido */
cabeza->valor = 10; /* acceso inválido */

/* Solución */
Nodo *cabeza = NULL;
if (!cabeza) {
  cabeza = malloc(sizeof(Nodo));
}

Siempre inicializa punteros en NULL y valida el resultado de malloc antes de usarlos.

9.2 Perder la referencia a la cabeza

Si el puntero a la cabeza se reasigna sin guardar la referencia original, la lista queda inaccesible, provocando fugas de memoria.

Nodo *cabeza = lista;
cabeza = cabeza->sig; /* Se "pierde" el nodo inicial si no se guarda */

/* Solución correcta */
Nodo *actual = cabeza;
cabeza = cabeza->sig;
free(actual);

9.3 Memory leaks

Ocurren cuando los nodos se reservan con malloc pero no se liberan. Con el tiempo, la memoria del proceso crece hasta agotar los recursos.

void lista_insertar(Lista *lista, int valor) {
  Nodo *n = malloc(sizeof(Nodo));
  /* ... */
}

/* Si no hay una función lista_limpiar, los nodos quedan sin liberar */

void lista_limpiar(Lista *lista) {
  Nodo *reco = lista->cabeza;
  while (reco) {
    Nodo *tmp = reco->sig;
    free(reco);
    reco = tmp;
  }
  lista->cabeza = NULL;
}

9.4 Acceso a memoria liberada

Intentar usar un nodo después de liberar su memoria genera errores impredecibles (“use-after-free”).

Nodo *victima = lista->cabeza;
lista->cabeza = victima->sig;
free(victima);
printf("%d", victima->valor); /* ERROR: memoria liberada */

/* Solución */
int valor = victima->valor;
free(victima);
printf("%d", valor);

9.5 Creación accidental de ciclos

Al manipular punteros, un error puede conectar un nodo consigo mismo o saltarse parte de la lista, creando un ciclo que provoca recorridos infinitos.

reco->sig = reco; /* ERROR: ciclo inmediato */

/* Verificar siempre antes de asignar */
if (reco->sig) {
  /* lógica normal */
}

Para detectar ciclos involuntarios, se pueden usar algoritmos como “tortuga y liebre” (Floyd) durante la depuración.

9.6 Punteros salvajes

Un puntero salvaje apunta a una región aleatoria de memoria debido a cast incorrectos, memoria liberada o falta de inicialización. Produce fallas sutiles o corruptelas.

Nodo *reco = (Nodo *)0x1234; /* Dirección arbitraria - ERROR */

/* Buenas prácticas */
Nodo *reco = NULL;
/* ... asignar reco con una dirección válida ... */
if (reco) {
  /* usar el puntero */
}

9.7 Lista de verificación rápida

  • Inicializa todos los punteros y valida malloc.
  • Mantén funciones para eliminar y limpiar la lista.
  • Evita acceder a punteros después de liberar su memoria.
  • Inspecciona los enlaces antes de reasignarlos para prevenir ciclos.
  • Usa herramientas como sanitizadores o Valgrind para detectar fugas y accesos indebidos.