El comportamiento LIFO se puede materializar con distintas estructuras internas. La elección depende de si buscamos límites claros, eficiencia en memoria o flexibilidad absoluta. En C las dos familias principales son basadas en arrays y basadas en listas enlazadas.
Utiliza un bloque contiguo para almacenar los elementos y un índice que representa el tope. Puede ser:
realloc cuando se llena.#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *datos;
int tope;
int capacidad;
} PilaArray;
PilaArray *crear_pila_array(int capacidad_inicial) {
PilaArray *p = malloc(sizeof(PilaArray));
if (!p) return NULL;
p->datos = malloc(sizeof(int) * capacidad_inicial);
if (!p->datos) { free(p); return NULL; }
p->capacidad = capacidad_inicial;
p->tope = 0;
return p;
}
void push_array(PilaArray *p, int valor) {
if (p->tope == p->capacidad) {
int nueva_cap = p->capacidad * 2;
int *tmp = realloc(p->datos, sizeof(int) * nueva_cap);
if (!tmp) return;
p->datos = tmp;
p->capacidad = nueva_cap;
}
p->datos[p->tope++] = valor;
}
int pop_array(PilaArray *p) {
if (p->tope == 0) return -1;
return p->datos[--p->tope];
}
int main(void) {
PilaArray *p = crear_pila_array(2);
push_array(p, 4);
push_array(p, 7);
push_array(p, 9);
printf("Pop: %d\n", pop_array(p));
printf("Pop: %d\n", pop_array(p));
free(p->datos);
free(p);
return 0;
}
La estrategia estática elimina la necesidad de malloc, mientras que la dinámica permite crecer bajo demanda.
En lugar de arrays, se reservan nodos individuales que se conectan mediante punteros. Cada push crea un nodo y lo enlaza al tope.
#include <stdio.h>
#include <stdlib.h>
typedef struct Nodo {
int valor;
struct Nodo *sig;
} Nodo;
typedef struct {
Nodo *tope;
} PilaNodos;
void push_nodo(PilaNodos *p, int valor) {
Nodo *nuevo = malloc(sizeof(Nodo));
if (!nuevo) return;
nuevo->valor = valor;
nuevo->sig = p->tope;
p->tope = nuevo;
}
int pop_nodo(PilaNodos *p) {
if (!p->tope) return -1;
Nodo *tmp = p->tope;
int valor = tmp->valor;
p->tope = tmp->sig;
free(tmp);
return valor;
}
int main(void) {
PilaNodos pila = { .tope = NULL };
push_nodo(&pila, 1);
push_nodo(&pila, 2);
push_nodo(&pila, 3);
printf("Pop: %d\n", pop_nodo(&pila));
printf("Pop: %d\n", pop_nodo(&pila));
printf("Pop: %d\n", pop_nodo(&pila));
return 0;
}
Esta técnica no impone una capacidad fija y libera memoria a medida que se desapila.
| Criterio | Pila con arrays | Pila con nodos |
|---|---|---|
| Reserva | Contigua, favorece cache | No contigua, se hace por nodo |
| Capacidad | Fija o ampliable con realloc | Crece según memoria disponible |
| Uso de memoria | Menos overhead, pero puede reservar espacio ocioso | Overhead por punteros, pero ajusta al número de elementos |
| Complejidad | Operaciones O(1); redimensionar cuesta O(n) | Operaciones O(1); sin costo de redimensionado |
Conocer ambas alternativas permite ajustar la pila a las restricciones reales del proyecto y cambiar de estrategia sin reescribir la interfaz pública.