2010-02-25 8 views
153

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

+72

Si no sabe de qué se trata, ¿cómo puede esperar que sepa que no son lo mismo para C y para C++? – dreamlax

+0

@dreamlax: Entonces, ¿cómo el concepto de "desenrollar la pila" es diferente en C & C++? – Destructor

+1

@PravasiMeet: C no tiene manejo de excepciones, por lo que el desenrollado de la pila es muy directo, sin embargo, en C++, si se produce una excepción o una función finaliza, el desenrollado de la pila implica la destrucción de objetos C++ con duración de almacenamiento automática. – dreamlax

Respuesta

115

El desenrollamiento de la pila generalmente se menciona en relación con el manejo de excepciones. He aquí 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í memoria asignada para pleak se perderá si se lanza una excepción, mientras que la memoria asignada a s será debidamente liberado por std::string destructor en cualquier caso. Los objetos asignados en la pila se "desenrollan" cuando se sale del alcance (aquí el alcance es de la función func).) Esto lo hace el compilador que inserta llamadas a destructores de variables automáticas (de pila).

Ahora bien, este es un concepto muy potente que conduce a la técnica llamada RAII, es decir Recursos adquisición es de inicialización, que nos ayuda a gestionar los recursos como la memoria, las conexiones de bases de datos, descriptores de archivos abiertos, etc., en C++.

Ahora que nos permite proporcionar exception safety guarantees.

+0

¡Eso fue realmente esclarecedor! Así que entiendo esto: si mi proceso se cuelga inesperadamente al salir CUALQUIER bloque en el que la pila se está copiando, puede ocurrir que el código después del código del controlador de excepción no se ejecute y pueda causar pérdidas de memoria , corrupción de montón, etc. –

+0

Hmm, crash es un poco diferente, aunque relacionado, animal (señales, volcados de núcleo, etc.) De lo que estoy hablando son herramientas C++/instalaciones para administrar de manera segura los recursos en presencia de excepciones/errores , que * podría * dar lugar a un bloqueo si no se maneja. –

+11

Si el programa "falla" (es decir, * finaliza * debido a un error), cualquier pérdida de memoria o corrupción de montón es irrelevante ya que la memoria se libera al finalizar. –

9

No sé si ha leído esto todavía, pero Wikipedia's article on the call stack tiene una explicación decente.

desenrollar:

Al regresar de la función llamada se abrirá el marco superior fuera de la pila, tal vez dejando un valor de retorno. El acto más general de extraer uno o más fotogramas de la pila para reanudar la ejecución en otro lugar del programa se llama desenrollar la pila y debe realizarse cuando se utilizan estructuras de control no locales, como las utilizadas para la gestión de excepciones. En este caso, el marco de pila de una función contiene una o más entradas que especifican manejadores de excepciones. Cuando se lanza una excepción, la pila 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 declaración goto global transfiera el control desde una función anidada a una función externa invocada anteriormente. Esta operación requiere que la pila se desenrolle, eliminando tantos marcos de pila 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 pila se desenrolla utilizando el operador especial de desenrollar y proteger.

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

Inspección [editar]

12

Pila de desenrollado es un C concepto mayoría ++, que trata de cómo los objetos STACK-asignado se destruyen cuando se sale de su ámbito de aplicación (ya sea normalmente, o a través de una excepción).

Digamos que tiene 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" 
+0

¿Esto se aplica a cualquier bloque? Quiero decir si solo hay { // algunos objetos locales } –

+0

@Rajendra: Sí, un bloque anónimo define un área de alcance, por lo que también cuenta. –

38

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

Sin embargo, específicamente en el caso de C++, el desenrollado de la pila 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.

+4

No hay nada especial sobre los bloques 'try'. Los objetos de pila asignados en _any_bloque (si 'try' o no) están sujetos a desenrollarse cuando el bloque sale. –

+0

Oh, mi mal. Cualquier bloque. : P – jrista

+0

Ha pasado un tiempo desde que hice mucha codificación C++. Tuve que sacar esa respuesta de las profundidades oxidadas. ; P – jrista

48

Todo esto se relaciona con C++:

Definición: A medida que crea objetos de forma estática (en la pila en lugar de su asignación en la memoria heap) y realizar llamadas de función, que se "apilan arriba".

Cuando se sale de un alcance (nada delimitada por { y }) (mediante el uso de return XXX;, alcanzando el final del ámbito o lanzar una excepción) todo dentro de ese ámbito se destruye (destructores se llaman para todo). Este proceso de destruir objetos locales y llamar a destructores se llama desenrollado de la pila.

Tiene las siguientes cuestiones relacionadas con la pila de desenrollar:

  1. pérdidas de memoria evitando (nada asigna de forma dinámica no gestionada por un objeto local y limpiado en el destructor será filtrada) - ver RAII referred to por Nikolai, y the documentation for boost::scoped_ptr o este ejemplo de uso de boost::mutex::scoped_lock.

  2. coherencia del programa: 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 pila nunca debe lanzar una excepción (utilice solo el código garantizado para no arrojar destructores, o rodee todo en los destructores con try { y } catch(...) {}).

Si alguna destructor lanza una excepción durante la pila relajarse al final en el tierra de comportamiento indefinido que podría causar su programa para treminate inesperadamente (comportamiento más común) o el universo a fin (teóricamente posible, pero tiene no se ha observado en la práctica todavía).

+2

Por el contrario. Si bien no se debe abusar de los gotos, estos causan que la pila se desenrolle en MSVC (no en GCC, por lo que es probable que sea una extensión). setjmp y longjmp hacen esto de forma cruzada, con algo menos de flexibilidad. –

+10

Acabo de probar esto con gcc y llama correctamente a los destructores cuando salgo de un bloque de código. Ver http://stackoverflow.com/questions/334780/are-goto-and-destructors-compatible/334781#334781 - como se menciona en ese enlace, esto también forma parte del estándar. – Damyan

+1

leyendo Nikolai, jrista y tu respuesta en este orden, ¡ahora tiene sentido! – n611x007

1

Cuando se lanza una excepción y el control pasa de un bloque try a un controlador, el tiempo de ejecución C++ llama a destructores para todos los objetos automáticos construidos desde el comienzo del bloque try. Este proceso se llama desenrollamiento de la pila. 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 programa sale del bloque en el que se declara x.)

Si se produce una excepción durante 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.

6

Todo el mundo ha hablado sobre el manejo de excepciones en C++.Pero, creo que hay otra connotación para desenrollar la pila y que está relacionada con la depuración. Un depurador tiene que deshacerse de la pila 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 fotograma actual. El ejemplo para esto podría ser comandos up/down/bt en gdb.

+5

La acción del depurador se suele llamar "Apilamiento en pila", que simplemente analiza la pila. El "Desenrollado de pila" implica no solo "Recorrer la pila" sino también invocar a los destructores de los objetos que existen en la pila. – Adisak

+0

@Adisak No sabía que también se llama "pila de caminar". Siempre he estado viendo "desenrollar la pila" en el contexto de todos los artículos de depuración y también dentro del código gdb. Me pareció más apropiado "desenrollar la pila", ya que no se trata solo de mirar la información de la pila para cada función, sino que implica desenrollar la información de la trama (c.f. CFI en enano). Esto se procesa en orden una función por una. – bbv

+0

Supongo que la "pila de caminar" se hace más famosa por Windows. Además, he encontrado un ejemplo de https://code.google.com/p/google-breakpad/wiki/StackWalking aparte del doc de la norma enana, que utiliza el término "desenrollar" algunas veces. Aunque estoy de acuerdo, es un desenrollamiento virtual. Además, la pregunta parece estar preguntando por cada significado posible que pueda sugerir "desenrollar la pila". – bbv

1

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

2

C++ runtime 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 pila en ese orden. Cuando f1() arroja, se invocan los destructores B y A.

#include <iostream> 
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; 
} 

La salida de este programa será

B's dtor 
A's dtor 

Esto se debe a que la pila de llamadas del programa cuando f1() throws parece

f1() 
f() 
main() 

lo tanto, cuando f1() se extrae, 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 codificación!

8

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

¿Qué es stack unwinding?

En cualquier lenguaje que soporte las funciones recursivas (es decir. casi todo excepto Fortran 77 y Brainf * ck) Language Runtime mantiene una pila de qué funciones están ejecutando actualmente. El desenrollado de la pila es una forma de inspeccionar, y posiblemente modificar, esa pila.

¿Por qué querrías hacer eso?

La respuesta puede parecer obvio, pero hay varios relacionados, pero sutilmente diferentes, situaciones en las que el desenrollado es útil o necesario:

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

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

Puede encontrar el artículo completo here.

1

OMI, la figura a continuación diagrama en este article bellamente explica el efecto de desenrollado de pila en la ruta de la siguiente instrucción (para ser ejecutado una vez que se produce una excepción que es no detectada):

enter image description here

En la imagen:

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

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

Cuestiones relacionadas