Las pilas son estructuras de datos lineales que modelan el principio de apilar y desapilar como en la vida cotidiana: colocar elementos uno sobre otro y retirarlos en orden inverso. En C podemos verlas como un Tipo de Dato Abstracto (TDA) que se define por su comportamiento antes que por su implementación interna.
En este primer tema aclaramos de forma conceptual qué resuelven, por qué se asocian con el acrónimo LIFO (Last In, First Out) y cuándo conviene preferirlas frente a colas o listas. También revisamos ejemplos prácticos para conectar la teoría con situaciones cotidianas y con el código que construiremos en los temas siguientes.
Una pila es un contenedor donde las inserciones (push) y las extracciones (pop) ocurren en un único extremo denominado tope. No se recorren posiciones intermedias ni se permite acceder directamente a elementos alejados del tope, lo que simplifica su implementación y asegura complejidad constante en las operaciones principales.
En C podemos representarla mediante un array y un marcador del tope, como muestra el siguiente ejemplo minimalista:
#define TAM_MAX 128
typedef struct {
int datos[TAM_MAX];
int tope;
} Pila;
void inicializar(Pila *p) {
p->tope = 0;
}
void push(Pila *p, int valor) {
if (p->tope == TAM_MAX) return; /* pila llena */
p->datos[p->tope++] = valor;
}
int pop(Pila *p) {
if (p->tope == 0) return -1; /* pila vacia */
return p->datos[--p->tope];
}
La estructura anterior muestra los componentes esenciales: almacenamiento, tope y funciones que controlan el flujo de datos. En implementaciones profesionales también se declaran funciones para consultar el elemento superior (peek) o para verificar si la pila está vacía.
Para compilarlo o probarlo en CLion basta con agregar una función main que ejecute una secuencia de operaciones controlada:
#include <stdio.h>
int main(void) {
Pila pila;
inicializar(&pila);
push(&pila, 10);
push(&pila, 20);
push(&pila, 30);
printf("Desapilado: %d\n", pop(&pila));
printf("Desapilado: %d\n", pop(&pila));
printf("Desapilado: %d\n", pop(&pila));
return 0;
}
CLion o cualquier otro IDE solo necesita este main para generar un ejecutable de consola, y las impresiones permiten verificar cómo se cumple el orden LIFO.
LIFO describe el orden de servicio de la pila: el último elemento insertado es el primero en salir. Ese comportamiento aparece en dominios como la ejecución de funciones (stack de llamadas), el manejo de expresiones algebraicas y la navegación por historiales.
pop, se obtiene 5 y el nuevo tope pasa a ser 7.Como no hay desplazamiento de elementos internos, cada operación requiere un paso O(1), lo que vuelve a las pilas ideales para resolver problemas que necesitan revertir el orden de datos o mantener historiales acotados.
Las pilas coexisten con otras estructuras lineales. La tabla siguiente resume las diferencias más relevantes:
| Característica | Pila | Cola | Lista enlazada |
|---|---|---|---|
| Extremo de inserción | Un solo extremo (tope) | Final de la cola | Posiciones arbitrarias mediante nodos |
| Extremo de extracción | El mismo tope | Comienzo de la cola | Depende de los enlaces definidos |
| Acceso aleatorio | No permitido | No permitido | Se puede recorrer nodo por nodo |
| Complejidad básica | O(1) push/pop | O(1) enqueue/dequeue | Inserción/eliminación O(1) si se conoce el nodo previo |
Elegir una u otra estructura depende del orden deseado: pilas para revertir, colas para mantener orden cronológico y listas cuando se requiere insertar o eliminar en posiciones intermedias.
Las pilas son intuitivas porque emulan procesos familiares:
Comprender estos paralelismos facilita visualizar la ejecución de algoritmos en C: cualquier flujo que requiera deshacer o volver sobre pasos previos puede traducirse al modelo de pilas con costos previsibles.