¿Uso correcto de stack y stack en C ++?

He estado progtwigndo por un tiempo pero ha sido principalmente Java y C #. Nunca tuve que administrar la memoria por mi cuenta. Recientemente comencé a progtwigr en C ++ y estoy un poco confundido sobre cuándo debo almacenar cosas en la stack y cuándo almacenarlas en el montón.

Según entiendo, las variables a las que se accede con mucha frecuencia se deben almacenar en la stack y los objetos, las variables que rara vez se usan y las estructuras de datos grandes deben almacenarse en el montón. ¿Es correcto o soy incorrecto?

No, la diferencia entre la stack y el montón no es el rendimiento. Su vida útil: cualquier variable local dentro de una función (todo lo que no sea malloc () o nuevo) vive en la stack. Se va cuando regresas 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.

class Thingy; Thingy* foo( ) { int a; // this int lives on the stack Thingy B; // this thingy lives on the stack and will be deleted when we return from foo Thingy *pointerToB = &B; // this points to an address on the stack Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap. // pointerToC contains its address. // this is safe: C lives on the heap and outlives foo(). // Whoever you pass this to must remember to delete it! return pointerToC; // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. // whoever uses this returned pointer will probably cause a crash! return pointerToB; } 

Para una comprensión más clara de lo que es la stack, acércate desde el otro extremo, en lugar de tratar de entender qué hace la stack en términos de un lenguaje de alto nivel, busca “call stack” y “calling convention” y mira qué la máquina realmente funciona cuando llamas a una función. La memoria de la computadora es solo una serie de direcciones; “montón” y “stack” son invenciones del comstackdor.

Yo diría:

Guárdelo en la stack, si PUEDE.

Guárdelo en el montón, si NECESITA.

Por lo tanto, prefiera la stack al montón. Algunas razones posibles por las que no puede almacenar algo en la stack son:

  • Es demasiado grande: en progtwigs multiproceso con sistema operativo de 32 bits, la stack tiene un tamaño pequeño y fijo (al menos en el tiempo de creación del hilo) (normalmente solo unos pocos megas. Esto es para poder crear muchos hilos sin agotar la dirección Para progtwigs de 64 bits o de subproceso único (Linux de todos modos), esto no es un problema importante. En Linux de 32 bits, los progtwigs de subprocesos únicos generalmente usan acumulaciones dinámicas que pueden seguir creciendo hasta llegar a la cima del montón.
  • Necesita acceder a él fuera del scope del marco de stack original: esta es realmente la razón principal.

Es posible, con comstackdores sensatos, asignar objetos de tamaño no fijo en el montón (generalmente matrices cuyo tamaño no se conoce en tiempo de comstackción).

Es más sutil de lo que sugieren las otras respuestas. No existe una división absoluta entre los datos en la stack y los datos en el montón basados ​​en cómo lo declaras. Por ejemplo:

 std::vector v(10); 

En el cuerpo de una función, eso declara un vector (matriz dinámica) de diez enteros en la stack. Pero el almacenamiento administrado por el vector no está en la stack.

Ah, pero (las otras respuestas sugieren) la vida útil de ese almacenamiento está limitada por la vida útil del vector , que aquí está basado en la stack, por lo que no importa cómo se implemente, solo podemos tratarlo como una stack. objeto con semántica de valores.

No tan. Supongamos que la función era:

 void GetSomeNumbers(std::vector &result) { std::vector v(10); // fill v with numbers result.swap(v); } 

Por lo tanto, cualquier elemento con una función de swap (y cualquier tipo de valor complejo debería tener uno) puede servir como un tipo de referencia reutilizable para algunos datos de almacenamiento dynamic, bajo 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 local desnudas. Todas las asignaciones de montón deben estar ocultas dentro de las clases.

Si lo hace, puede pensar en todas las variables de su progtwig como si fueran simples tipos de valores, y olvidarse por completo del montón (excepto cuando escribe una nueva clase contenedora similar a un valor para algunos datos del montón, lo que debería ser inusual) .

Simplemente debe conservar un poco de conocimiento especial para ayudarlo a optimizar: cuando sea posible, en lugar de asignar una variable a otra como esta:

 a = b; 

cambiarlos así:

 a.swap(b); 

porque es mucho más rápido y no arroja excepciones. El único requisito es que no necesite b para continuar manteniendo el mismo valor (en cambio, obtendrá el valor de uno, que se descartaría en a = b ).

La desventaja es que este enfoque 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 shared_ptr que ya está en tr1. (Aunque yo argumentaría que si parece que lo necesita, posiblemente se haya alejado del punto óptimo de aplicabilidad de Standard C ++).

También podría almacenar un elemento en el montón si necesita ser utilizado fuera del scope de la función en la que se creó. Una expresión idiomática utilizada con los objetos de la stack se denomina RAII: esto implica utilizar el objeto basado en la stack como un contenedor para un recurso, cuando el objeto se destruye, el recurso se limpia. Los objetos basados ​​en stack son más fáciles de seguir cuando podría estar lanzando excepciones: no necesita preocuparse por eliminar un objeto basado en el montón en un manejador de excepciones. Esta es la razón por la que los punteros sin formato no se usan normalmente en C ++ moderno, se usaría un puntero inteligente que puede ser un contenedor basado en stack para un puntero sin formato a un objeto basado en el montón.

Para agregar a las otras respuestas, también puede tratarse del rendimiento, al menos un poco. No es que deba preocuparse por esto a menos que sea relevante para usted, pero:

Asignar 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 gastos generales). Esto puede ser más lento a medida que la memoria se fragmenta o se acerca al 100% de su espacio de direcciones. Por otro lado, las asignaciones de stack son de tiempo constante, básicamente operaciones “libres”.

Otra cosa a considerar (de nuevo, realmente solo es importante si se convierte en un problema) es que generalmente el tamaño de la stack es fijo y puede ser mucho menor 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 se queda sin espacio en la stack, el tiempo de ejecución arrojará la excepción titular del sitio. No suele ser un gran problema, pero hay otra cosa que considerar.

Stack es más eficiente y más fácil de manejar los datos de scope.

Pero heap debería usarse para algo más grande que unos pocos KB (es fácil en C ++, solo crea un boost::scoped_ptr en la stack para mantener un puntero a la memoria asignada).

Considere un algoritmo recursivo que sigue llamando a sí mismo. ¡Es muy difícil limitar y / o adivinar el uso total de la stack! Mientras que en el montón, el asignador ( malloc() o new ) puede indicar throw de memoria devolviendo NULL o throw .

Fuente : Linux Kernel cuya stack no es más grande que 8KB.

Para completar, puede leer el artículo de Miro Samek sobre los problemas de usar el montón en el contexto del software integrado .

Un montón de problemas

La opción de asignar en el montón o en la stack está hecha 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 stack.

En mi opinión, hay dos factores decisivos

 1) Scope of variable 2) Performance. 

Preferiría usar stack en la mayoría de los casos, pero si necesita acceder a un scope externo variable, puede usar heap.

Para mejorar el rendimiento durante el uso de montones, también puede usar la funcionalidad para crear bloque de montones y eso puede ayudar a obtener rendimiento en lugar de asignar cada variable en una ubicación de memoria diferente.

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 lo guiará 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/