Las pilas en las arquitecturas informáticas son regiones de memoria donde se agregan o eliminan datos según el método "último en entrar, primero en salir" (LIFO) .
En la mayoría de los sistemas informáticos modernos, cada subproceso tiene una región reservada de memoria denominada pila. Cuando se ejecuta una función, puede agregar algunos de sus datos de estado local a la parte superior de la pila; cuando la función sale, es responsable de eliminar esos datos de la pila. Como mínimo, la pila de un subproceso se utiliza para almacenar la ubicación de una dirección de retorno proporcionada por el llamador para permitir que las instrucciones de retorno regresen a la ubicación correcta.
La pila se utiliza a menudo para almacenar variables de longitud fija locales para las funciones activas en ese momento. Los programadores pueden optar además por utilizar explícitamente la pila para almacenar datos locales de longitud variable. Si una región de memoria se encuentra en la pila del hilo, se dice que esa memoria ha sido asignada en la pila, es decir, asignación de memoria basada en pila (SBMA). Esto contrasta con una asignación de memoria basada en montón (HBMA). La SBMA a menudo está estrechamente acoplada con una pila de llamadas de función .
Debido a que los datos se agregan y eliminan de manera tal que "el último en entrar es el primero en salir", la asignación de memoria basada en pila es muy simple y, por lo general, mucho más rápida que la asignación de memoria basada en montón (también conocida como asignación de memoria dinámica ), por ejemplo, en C.malloc
Otra característica es que la memoria en la pila se recupera automáticamente y de manera muy eficiente cuando la función sale, lo que puede ser conveniente para el programador si los datos ya no son necesarios. [1] (Lo mismo se aplica a longjmp si se movió a un punto antes de que alloca
ocurriera la llamada). Sin embargo, si los datos deben conservarse en alguna forma, entonces deben copiarse de la pila al montón antes de que la función salga. Por lo tanto, la asignación basada en la pila es adecuada para datos temporales o datos que ya no son necesarios después de que la función actual sale.
El tamaño de pila asignado a un subproceso puede ser tan pequeño como unos pocos bytes en algunas CPU pequeñas. Asignar más memoria en la pila de la que está disponible puede provocar un bloqueo debido a un desbordamiento de pilaalloca
. Esta es también la razón por la que se suele impedir que se incorporen funciones que usan : [2] si se incorporen funciones de este tipo a un bucle, el invocador sufriría un crecimiento imprevisto en el uso de la pila, lo que haría que un desbordamiento fuera mucho más probable.
La asignación basada en pila también puede causar problemas menores de rendimiento: conduce a marcos de pila de tamaño variable, por lo que tanto los punteros de pila como de marco deben administrarse (con marcos de pila de tamaño fijo, el puntero de pila es redundante debido a la multiplicación del puntero de marco de pila por el tamaño de cada marco). Esto suele ser mucho menos costoso que llamar malloc
a y free
de todos modos. En particular, si la función actual contiene tanto llamadas a alloca
como bloques que contienen datos locales de longitud variable, entonces ocurre un conflicto entre los intentos de alloca de aumentar el marco de pila actual hasta que la función actual salga frente a la necesidad del compilador de colocar variables locales de longitud variable en la misma ubicación en el marco de pila. Este conflicto normalmente se resuelve creando una cadena separada de almacenamiento de montón para cada llamada a alloca
. [3] La cadena registra la profundidad de pila en la que se produce cada asignación, las llamadas posteriores a alloca
en cualquier función recortan esta cadena a la profundidad de pila actual para eventualmente (pero no inmediatamente) liberar cualquier almacenamiento en esta cadena. Una llamada a alloca
con un argumento de cero también se puede utilizar para activar la liberación de memoria sin asignar más memoria de ese tipo. Como consecuencia de este conflicto entre alloca
y el almacenamiento de variables locales, usar alloca
podría no ser más eficiente que usar malloc
.
Muchos sistemas similares a Unix, así como Microsoft Windows, implementan una función llamada alloca
para asignar dinámicamente memoria de pila de una manera similar a la basada en heap malloc
. Un compilador normalmente la traduce a instrucciones en línea que manipulan el puntero de pila, de manera similar a cómo se manejan las matrices de longitud variable . [4] Aunque no hay necesidad de liberar explícitamente la memoria, existe el riesgo de un comportamiento indefinido debido al desbordamiento de pila. [5] La función estaba presente en sistemas Unix desde 32/V (1978), pero no es parte del estándar C ni de ningún estándar POSIX .
En Microsoft Windows existe una versión más segura de alloca
called , que asigna en el montón si el tamaño de asignación es demasiado grande y reporta errores de desbordamiento de pila. Requiere el uso de . [6] gnulib proporciona una interfaz equivalente, aunque en lugar de lanzar una excepción SEH en caso de desbordamiento, delega a cuando se detecta un tamaño demasiado grande. [7] Se puede emular una característica similar usando contabilidad manual y verificación de tamaño, como en los usos de en glibc. [8]_malloca
_freea
malloc
alloca_account
Algunas familias de procesadores, como x86 , tienen instrucciones especiales para manipular la pila del subproceso que se está ejecutando actualmente. Otras familias de procesadores, como RISC-V , PowerPC y MIPS , no tienen compatibilidad explícita con la pila, sino que se basan en la convención y delegan la gestión de la pila a la interfaz binaria de aplicación (ABI) del sistema operativo .
Además, desde la versión C99 (opcional desde C11), es posible crear una matriz en la pila dentro de una función, de forma automática, conocida como auto VLA ( variable-length array ). [9]
void f ( int arrayLength ) { int b [ arrayLength ]; // VLA automático: la longitud de esta matriz se establece en el momento de la invocación de la función/generación de la pila. for ( int i = 0 ; i < arrayLength ; i ++ ) b [ i ] = 1 ; // al final de esta función, b[] está dentro del marco de la pila y desaparecerá cuando la función salga, por lo que no se requiere una llamada explícita a free(). }