¿Qué es el desenrollamiento de la stack?

¿Qué es el desenrollamiento de la stack? Buscó pero no pudo encontrar una respuesta esclarecedora.

El desenrollado de la stack generalmente se menciona en relación con el manejo de excepciones. Aquí hay un ejemplo:

void func( int x ) { char* pleak = new char[1024]; // might be lost => memory leak std::string s( "hello world" ); // will be properly destructed if ( x ) throw std::runtime_error( "boom" ); delete [] pleak; // will only get here if x == 0. if x!=0, throw exception } int main() { try { func( 10 ); } catch ( const std::exception& e ) { return 1; } return 0; } 

Aquí la memoria asignada para pleak se perderá si se lanza una excepción, mientras que la memoria asignada a s será liberada adecuadamente por std::string destructor en cualquier caso. Los objetos asignados en la stack se “desenrollan” cuando se sale del scope (aquí el scope es de la función func .) Esto lo hace el comstackdor que inserta llamadas a destructores de variables automáticas (de stack).

Ahora bien, este es un concepto muy poderoso que lleva a la técnica llamada RAII , es la Inicialización de Adquisición de Recursos , que nos ayuda a administrar recursos como la memoria, conexiones de bases de datos, descriptores de archivos abiertos, etc. en C ++.

Ahora eso nos permite proporcionar garantías de seguridad excepcionales .

Todo esto se relaciona con C ++:

Definición : A medida que crea objetos estáticamente (en la stack en lugar de asignarlos en la memoria del montón) y realiza llamadas a funciones, se “astackn”.

Cuando se sale de un ámbito (cualquier cosa delimitada por { y } ) (utilizando return XXX; llegando al final del scope o lanzando una excepción) se destruye todo dentro de ese scope (se invocan destructores para todo). Este proceso de destruir objetos locales y llamar a destructores se llama desenrollado de stack.

Tiene los siguientes problemas relacionados con el desenrollado de la stack:

  1. evitando pérdidas de memoria (todo lo que se haya asignado dinámicamente que no esté gestionado por un objeto local y se haya limpiado en el destructor se filtrará) – vea RAII referido por Nikolai, y la documentación para boost :: scoped_ptr o este ejemplo de uso de boost :: mutex :: scoped_lock .

  2. Consistencia del progtwig: las especificaciones de C ++ establecen que nunca debe lanzar una excepción antes de que se haya manejado una excepción existente. Esto significa que el proceso de desenrollado de la stack nunca debe arrojar una excepción (ya sea que use solo código garantizado para no arrojar destructores, o rodee todo en los destructores con try { y } catch(...) {} ).

Si cualquier destructor arroja una excepción durante el desenrollamiento de la stack, terminará en un terreno de comportamiento indefinido que podría hacer que su progtwig trepe inesperadamente (el comportamiento más común) o que el universo termine (teóricamente posible pero aún no se ha observado en la práctica).

En un sentido general, una stack “desenrollar” es casi sinónimo del final de una llamada de función y el estallido posterior de la stack.

Sin embargo, específicamente en el caso de C ++, el desenrollado de la stack tiene que ver con cómo C ++ llama a los destructores para los objetos asignados desde el comienzo de cualquier bloque de código. Los objetos que se crearon dentro del bloque se desasignan en el orden inverso de su asignación.

El desenrollado de la stack es principalmente un concepto de C ++, que trata sobre cómo se destruyen los objetos asignados por la stack cuando se abandona su scope (normalmente o a través de una excepción).

Digamos que tienes este fragmento de código:

 void hw() { string hello("Hello, "); string world("world!\n"); cout < < hello << world; } // at this point, "world" is destroyed, followed by "hello" 

No sé si aún lees esto, pero el artículo de Wikipedia sobre la stack de llamadas tiene una explicación decente.

Desbobinar:

Al regresar de la función llamada se abrirá el marco superior de la stack, quizás dejando un valor de retorno. El acto más general de extraer uno o más marcos de la stack para reanudar la ejecución en otro lugar del progtwig se llama desenrollado de stack y se debe realizar cuando se usan estructuras de control no locales, como las utilizadas para el manejo de excepciones. En este caso, el marco de stack de una función contiene una o más entradas que especifican manejadores de excepciones. Cuando se lanza una excepción, la stack se desenrolla hasta que se encuentra un controlador que está preparado para manejar (atrapar) el tipo de la excepción lanzada.

Algunos idiomas tienen otras estructuras de control que requieren un desenrollado general. Pascal permite que una statement goto global transfiera el control desde una función anidada a una función externa invocada anteriormente. Esta operación requiere que la stack se desenrolle, eliminando tantos marcos de stack como sea necesario para restaurar el contexto adecuado para transferir el control a la instrucción de destino dentro de la función externa adjunta. Del mismo modo, C tiene las funciones setjmp y longjmp que actúan como gotos no locales. Common Lisp permite el control de lo que sucede cuando la stack se desenrolla utilizando el operador especial de desenrollar y proteger.

Al aplicar una continuación, la stack se desenrolla (lógicamente) y luego se rebobina con la stack de la continuación. Esta no es la única forma de implementar continuaciones; por ejemplo, al usar stacks múltiples y explícitas, la aplicación de una continuación simplemente puede activar su stack e impulsar un valor para pasar. El lenguaje de progtwigción Scheme permite que se ejecuten thunk arbitrarios en puntos específicos al “desenrollar” o “rebobinar” la stack de control cuando se invoca una continuación.

Inspección [editar]

Leí una publicación de blog que me ayudó a entender.

¿Qué es el desenrollamiento de la stack?

En cualquier idioma que admita funciones recursivas (es decir, casi todo, excepto Fortran 77 y Brainf * ck), el tiempo de ejecución del lenguaje mantiene una stack de las funciones que se están ejecutando actualmente. El desenrollado de la stack es una forma de inspeccionar, y posiblemente modificar, esa stack.

¿Por qué querrías hacer eso?

La respuesta puede parecer obvia, pero hay varias situaciones relacionadas, aunque sutilmente diferentes, en las que el desenrollamiento es útil o necesario:

  1. Como un mecanismo de flujo de control de tiempo de ejecución (excepciones de C ++, C longjmp (), etc.).
  2. En un depurador, para mostrar al usuario la stack.
  3. En un generador de perfiles, para tomar una muestra de la stack.
  4. Desde el progtwig en sí (como desde un controlador de locking para mostrar la stack).

Estos tienen requisitos sutilmente diferentes. Algunos de estos son críticos para el rendimiento, otros no. Algunos requieren la capacidad de reconstruir registros desde el marco externo, otros no. Pero entraremos en todo eso en un segundo.

Puedes encontrar la publicación completa aquí .

Todos han hablado sobre el manejo de excepciones en C ++. Pero, creo que hay otra connotación para desenrollar la stack y que está relacionada con la depuración. Un depurador tiene que deshacerse de la stack cada vez que se supone que va a un marco anterior al marco actual. Sin embargo, esto es una especie de desenrollado virtual, ya que necesita rebobinarse cuando vuelve al fotogtwig actual. El ejemplo para esto podría ser comandos up / down / bt en gdb.

El tiempo de ejecución de C ++ destruye todas las variables automáticas creadas entre throw & catch. En este ejemplo simple debajo de f1 () arroja y captura principal (), entre los objetos de tipo B y A se crean en la stack en ese orden. Cuando f1 () arroja, se invocan los destructores B y A.

 #include  using namespace std; class A { public: ~A() { cout < < "A's dtor" << endl; } }; class B { public: ~B() { cout << "B's dtor" << endl; } }; void f1() { B b; throw (100); } void f() { A a; f1(); } int main() { try { f(); } catch (int num) { cout << "Caught exception: " << num << endl; } return 0; } 

El resultado de este progtwig será

 B's dtor A's dtor 

Esto se debe a que la stack de llamadas del progtwig cuando arroja f1 () se ve como

 f1() f() main() 

Por lo tanto, cuando se quita f1 (), la variable automática b se destruye, y luego cuando se quita f () la variable automática a se destruye.

Espero que esto ayude, ¡feliz encoding!

IMO, el siguiente diagtwig en este artículo explica maravillosamente el efecto del desenrollado de la stack en la ruta de la siguiente instrucción (que se ejecutará una vez que se lanza una excepción que no se ha capturado):

enter image description here

En la foto:

  • La primera es una ejecución de llamada normal (sin excepción).
  • Abajo, cuando se lanza una excepción.

En el segundo caso, cuando se produce una excepción, la stack de llamadas de función se busca linealmente para el manejador de excepciones. La búsqueda finaliza en la función con controlador de excepciones, es decir, main() con el bloque try-catch adjunto, pero no antes de eliminar todas las entradas anteriores de la stack de llamadas a funciones.

Cuando se lanza una excepción y el control pasa de un bloque try a un manejador, el tiempo de ejecución C ++ llama a los destructores para todos los objetos automáticos construidos desde el comienzo del bloque try. Este proceso se llama desenrollamiento de la stack. Los objetos automáticos se destruyen en orden inverso a su construcción. (Los objetos automáticos son objetos locales que han sido declarados automáticos o registrados, o no declarados estáticos o externos. Un objeto automático x se elimina cada vez que el progtwig sale del bloque en el que se declara x).

Si se lanza una excepción durante la construcción de un objeto que consiste en subobjetos o elementos de matriz, los destructores solo se llaman para aquellos subobjetos o elementos de matriz construidos con éxito antes de que se lanzara la excepción. Un destructor para un objeto estático local solo se invocará si el objeto se construyó correctamente.

En Java, la stack que se deshace o deshace no es muy importante (con el recolector de basura). En muchos documentos de manejo de excepciones, vi este concepto (desenrollado de la stack), en especial, aquellos writers tratan con el manejo de excepciones en C o C ++. con try catch blocks que no debemos olvidar: stack gratis de todos los objetos después de bloques locales .