He estado programando durante un tiempo, pero ha sido principalmente Java y C #. En realidad, nunca tuve que administrar la memoria por mi cuenta. Recientemente comencé a programar en C ++ y estoy un poco confundido sobre cuándo debería almacenar cosas en la pila y cuándo almacenarlas en el montón.
Según tengo entendido, las variables a las que se accede con mucha frecuencia deben almacenarse en la pila y los objetos, las variables raramente utilizadas y las grandes estructuras de datos deben almacenarse en el montón. ¿Es esto correcto o soy incorrecto?
Respuestas:
No, la diferencia entre stack y heap no es el rendimiento. Es vida útil: cualquier variable local dentro de una función (cualquier cosa que no malloc () o nueva) vive en la pila. Desaparece cuando regresa de la función. Si desea que algo viva más tiempo que la función que lo declaró, debe asignarlo en el montón.
Para una comprensión más clara de lo que es la pila, acérquese desde el otro extremo, en lugar de tratar de entender lo que hace la pila en términos de un lenguaje de alto nivel, busque "pila de llamadas" y "convención de llamadas" y vea qué la máquina realmente lo hace cuando llamas a una función. La memoria de la computadora es solo una serie de direcciones; "Heap" y "stack" son invenciones del compilador.
fuente
Yo diría:
Guárdelo en la pila, si PUEDE.
Guárdelo en el montón, si lo NECESITA.
Por lo tanto, prefiera la pila al montón. Algunas razones posibles por las que no puede almacenar algo en la pila son:
Es posible, con compiladores sensibles, asignar objetos de tamaño no fijo en el montón (generalmente matrices cuyo tamaño no se conoce en tiempo de compilación).
fuente
Es más sutil de lo que sugieren las otras respuestas. No hay una división absoluta entre los datos en la pila y los datos en el montón en función de cómo lo declare. Por ejemplo:
En el cuerpo de una función, que declara una
vector
(matriz dinámica) de diez enteros en la pila. Pero el almacenamiento gestionado por elvector
no está en la pila.Ah, pero (las otras respuestas sugieren) la vida útil de ese almacenamiento está limitada por la vida útil del
vector
mismo, que aquí está basada en la pila, por lo que no importa cómo se implemente; solo podemos tratarlo como un objeto basado en la pila con valor semántico.No tan. Supongamos que la función era:
Por lo tanto, cualquier cosa que tenga una
swap
función (y cualquier tipo de valor complejo debería tener una) puede servir como una especie de referencia modificable a algunos datos de almacenamiento dinámico, en un sistema que garantiza un único propietario de esos datos.Por lo tanto, el enfoque moderno de C ++ es nunca almacenar la dirección de los datos del montón en variables de puntero locales desnudas. Todas las asignaciones de montón deben estar ocultas dentro de las clases.
Si hace eso, puede pensar en todas las variables en su programa como si fueran tipos de valores simples, y olvidarse del montón por completo (excepto al escribir una nueva clase de contenedor de valor para algunos datos de montón, lo que debería ser inusual) .
Simplemente tiene que retener un conocimiento especial para ayudarlo a optimizar: cuando sea posible, en lugar de asignar una variable a otra como esta:
intercambiarlos así:
porque es mucho más rápido y no arroja excepciones. El único requisito es que no necesita
b
continuar manteniendo el mismo valor (en sua
lugar, obtendrá el valor, que se tiraría a la basuraa = b
).La desventaja es que este enfoque lo obliga a devolver valores de funciones a través de parámetros de salida en lugar del valor de retorno real. Pero lo están arreglando en C ++ 0x con referencias rvalue .
En las situaciones más complicadas de todas, llevaría esta idea al extremo general y usaría una clase de puntero inteligente como la
shared_ptr
que ya está en tr1. (Aunque diría que si parece que lo necesita, posiblemente se haya mudado fuera del punto ideal de aplicabilidad de Standard C ++).fuente
También almacenaría un elemento en el montón si necesita ser utilizado fuera del alcance de la función en la que se crea. Una expresión idiomática utilizada con los objetos de la pila se llama RAII: esto implica el uso del objeto basado en la pila como un contenedor para un recurso, cuando el objeto se destruye, el recurso se limpiará. Los objetos basados en la pila son más fáciles de rastrear cuando podría estar lanzando excepciones: no necesita preocuparse por eliminar un objeto basado en el montón en un controlador de excepciones. Esta es la razón por la cual los punteros sin formato no se usan normalmente en C ++ moderno, usaría un puntero inteligente que puede ser un contenedor basado en pila para un puntero sin formato a un objeto basado en el montón.
fuente
Para agregar a las otras respuestas, también puede tratarse sobre el rendimiento, al menos un poco. No es que deba preocuparse por esto a menos que sea relevante para usted, pero:
La asignación en el montón requiere encontrar un seguimiento de un bloque de memoria, que no es una operación de tiempo constante (y toma algunos ciclos y sobrecarga). Esto puede ser más lento a medida que la memoria se fragmenta y / o se está acercando al uso del 100% de su espacio de direcciones. Por otro lado, las asignaciones de pila son operaciones de tiempo constante, básicamente "libres".
Otra cosa a tener en cuenta (nuevamente, solo es importante si se convierte en un problema) es que, por lo general, el tamaño de la pila es fijo y puede ser mucho más bajo que el tamaño del montón. Entonces, si está asignando objetos grandes o muchos objetos pequeños, probablemente quiera usar el montón; si te quedas sin espacio de pila, el tiempo de ejecución arrojará la excepción titular del sitio. No suele ser un gran problema, pero otra cosa a tener en cuenta.
fuente
Stack es más eficiente y más fácil de administrar los datos de ámbito.
Pero el montón debería usarse para algo más grande que unos pocos KB (es fácil en C ++, solo cree un
boost::scoped_ptr
en la pila para mantener un puntero a la memoria asignada).Considere un algoritmo recursivo que sigue llamando a sí mismo. ¡Es muy difícil limitar o adivinar el uso total de la pila! Mientras que en el montón, el asignador (
malloc()
onew
) puede indicar falta de memoria al regresarNULL
othrow
ing.Fuente : Kernel de Linux cuya pila no es mayor de 8 KB.
fuente
std::unique_ptr
, que debería preferirse a cualquier biblioteca externa como Boost (aunque eso alimenta las cosas al estándar con el tiempo).Para completar, puede leer el artículo de Miro Samek sobre los problemas de usar el montón en el contexto del software incorporado .
Un montón de problemas
fuente
La opción de asignar en el montón o en la pila es la que se hace para usted, dependiendo de cómo se asigne su variable. Si asigna algo dinámicamente, utilizando una llamada "nueva", está asignando desde el montón. Si asigna algo como una variable global o como un parámetro en una función, se asigna en la pila.
fuente
En mi opinión, hay dos factores decisivos.
Preferiría usar stack en la mayoría de los casos, pero si necesita acceso a variables fuera del alcance, puede usar heap.
Para mejorar el rendimiento al usar montones, también puede usar la funcionalidad para crear bloque de almacenamiento dinámico y eso puede ayudar a obtener rendimiento en lugar de asignar cada variable en una ubicación de memoria diferente.
fuente
probablemente esto ha sido respondido bastante bien. Me gustaría señalarle la siguiente serie de artículos para tener una comprensión más profunda de los detalles de bajo nivel. Alex Darby tiene una serie de artículos, donde te guía con un depurador. Aquí está la Parte 3 sobre la Pila. http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/
fuente