Quel est le déroulement de pile?

qu'est-ce que stack unwinding? Fouillé, mais ne pouvait pas trouver de réponse instructive!

158
demandé sur R. Martinho Fernandes 2010-02-25 06:04:24

11 réponses

déverrouillage de pile est généralement parlé en relation avec la manipulation d'exception. Voici un exemple:

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;
}

ici la mémoire allouée pour pleak sera perdue si une exception est lancée, tandis que la mémoire attribuée à s sera correctement libérée par std::string destructeur dans tous les cas. Les objets alloués sur la pile sont "déroulé" quand le champ est sorti (ici la portée de la fonction func .), C'est fait par le compilateur insertion d'appels aux destructeurs de variables automatiques (stack).

maintenant c'est un concept très puissant conduisant à la technique appelée RAII , c'est-à-dire L'Acquisition de ressources est L'initialisation , qui nous aide à gérer des ressources comme la mémoire, les connexions de base de données, les descripteurs de fichiers ouverts, etc. en C++.

maintenant qui nous permet de fournir des garanties de sécurité d'exception .

124
répondu Nikolai Fetissov 2018-09-11 15:52:27

tout cela se rapporte à C++:

Définition : Comme vous créez des objets statiquement (sur la pile au lieu de les allouer dans la mémoire tas) et effectuez des appels de fonction, ils sont "empilés".

quand une portée (n'importe quoi délimité par { et } ) est sortie (en utilisant return XXX; , atteindre la fin de la portée ou lancer une exception) tout dans cette portée est détruit (destructeurs sont appelé pour tout). ce processus de destruction d'objets locaux et d'appel aux destructeurs est appelé déroulage de pile.

vous avez les problèmes suivants liés à stack unwinding:

  1. "éviter les fuites de mémoire (tout ce qui n'est pas géré dynamiquement par un objet local et nettoyé dans le destructeur sera divulgué) - voir Rai référé à par Nikolai, et la documentation pour boost::scoped_ptr ou cet exemple d'utilisation de boost::mutex::scoped_lock .

  2. cohérence du programme: les spécifications C++ précisent que vous ne devez jamais jeter une exception avant qu'une exception existante ait été traitée. Cela signifie que le processus de dévidage de la pile ne devrait jamais jeter une exception (soit utiliser seulement le code garanti de ne pas jeter dans destructeurs, ou entourer tout dans destructeurs avec try { et } catch(...) {} ).

si un destructeur lance une exception pendant le déroulement de la pile, vous finissez dans le land of unfined behavior qui pourrait causer votre programme à tréminer de façon inattendue (comportement le plus commun) ou l'univers à la fin (théoriquement possible, mais n'a pas encore été observé dans la pratique).

54
répondu utnapistim 2018-01-19 17:53:33

dans un sens général, une pile" décompresser " est à peu près synonyme de la fin d'un appel de fonction et de l'éclatement subséquent de la pile.

cependant, spécifiquement dans le cas de C++, le déroulement de la pile a à voir avec la façon dont C++ appelle les destructeurs pour les objets alloués depuis le début de n'importe quel bloc de code. Les objets qui ont été créés dans le bloc sont déalloqués dans l'ordre inverse de leur attribution.

38
répondu jrista 2010-02-25 03:10:43

déverrouillage de la pile est un concept principalement C++, qui traite de la façon dont les objets alloués à la pile sont détruits lorsque leur portée est épuisée (soit normalement, soit par une exception).

dites que vous avez ce fragment de code:

void hw() {
    string hello("Hello, ");
    string world("world!\n");
    cout << hello << world;
} // at this point, "world" is destroyed, followed by "hello"
12
répondu Chris Jester-Young 2010-02-25 03:07:54

Je ne sais pas si vous avez encore lu ceci, mais L'article de Wikipedia sur la pile d'appels a une explication décente.

Déroulement:

de retour de la fonction appelée pop le cadre supérieur de la pile, laissant peut-être une valeur de retour. L'acte plus général d'enlever un ou plusieurs cadres de la pile pour reprendre l'exécution ailleurs dans le programme est appelé pile le déroulement du doit être effectué lorsque des structures de commande non locales, telles que celles utilisées pour la manipulation des exceptions, sont utilisées. Dans ce cas, le cadre de pile d'une fonction contient une ou plusieurs entrées spécifiant des gestionnaires d'exception. Lorsqu'une exception est lancée, la pile est déverrouillée jusqu'à ce qu'un manipulateur soit trouvé qui est prêt à manipuler (attraper) le type de l'exception lancée.

certaines langues ont d'autres structures de contrôle qui nécessitent un déroulement général. Pascal permet à une instruction globale goto de transférer le contrôle d'une fonction imbriquée dans une fonction externe déjà invoquée. Cette opération exige que la pile soit déboulée, en enlevant autant de cadres de pile que nécessaire pour restaurer le contexte approprié pour transférer le contrôle à l'instruction cible à l'intérieur de la fonction externe enveloppante. De même, C a les fonctions setjmp et longjmp qui agissent en tant que Goto non local. Le Lisp commun permet de contrôler ce qui se passe lorsque la pile est déverrouillée en utilisant le unwind-protect particulière de l'opérateur.

lors de l'application d'une continuation, la pile est (logiquement) déverrouillée puis rebondie avec la pile de la continuation. Ce n'est pas la seule façon de mettre en œuvre des continuations; par exemple, en utilisant des piles multiples et explicites, l'application d'une continuation peut simplement activer sa pile et wind une valeur à passer. Le langage de programmation Scheme permet d'exécuter des tâches arbitraires en des points spécifiés sur "unwinding" ou" rewinding " de la contrôle de la pile lorsqu'une poursuite est invoquée.

"151900920 d'Inspection" [modifier]

9
répondu John Weldon 2017-05-16 10:37:25

j'ai lu un billet de blog qui m'a aidé à comprendre.

Qu'est-ce que stack unwinding?

dans toute langue supportant des fonctions récursives (c.-à-d. assez bien tout sauf Fortran 77 et Brainf*ck) le langage runtime garde une pile des fonctions actuellement exécutées. Le déroulement de la pile est une façon d'inspecter, et peut-être de modifier, cette pile.

Pourquoi voulez-vous faire ça?

La réponse peut sembler évidente, mais il en existe plusieurs, mais subtilement différentes, situations où le déroulement est utile ou nécessaire:

  1. comme mécanisme de contrôle de l'écoulement (exceptions C++, C longjmp(), etc.).
  2. Dans un débogueur, pour montrer à l'utilisateur la pile.
  3. dans un profileur, pour prélever un échantillon de la pile.
  4. du programme lui-même (comme dans un handler crash pour montrer la pile).

ceux-ci ont des exigences subtilement différentes. Certaines d'entre elles sont essentielles au rendement, d'autres non. Certains exigent l' possibilité de reconstruire des registres à partir du cadre extérieur, certains ne le font pas. Mais nous allons entrer dans tous ça en une seconde.

vous pouvez trouver le message complet ici .

8
répondu L. Langó 2014-05-08 19:22:13

tout le monde a parlé de la gestion des exceptions en C++. Mais, je pense qu'il y a une autre connotation pour le déroulement de la pile et qui est liée au débogage. Un débogueur doit faire le déroulement de la pile à chaque fois qu'il est censé aller à un cadre antérieur au cadre actuel. Cependant, il s'agit d'une sorte de déroulement virtuel car il a besoin de rembobiner quand il revient à la trame actuelle. L'exemple pour cela pourrait être des commandes up/down/bt dans gdb.

6
répondu bbv 2011-03-08 06:01:47

C++ runtime détruit toutes les variables automatiques créées entre throw & catch. Dans cet exemple simple ci-dessous f1() lancements et main() captures, entre les objets de type B et A sont créés sur la pile dans cet ordre. Quand f1 () lance, Les Destructeurs de B et A sont appelés.

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

le résultat de ce programme sera

B's dtor
A's dtor

c'est parce que la callstack du programme quand F1 () lance ressemble à

f1()
f()
main()

ainsi, quand f1() est déclenché, la variable automatique b est détruite, et alors quand f() est déclenché, la variable automatique a est détruite.

Espère que cette aide, bon codage!

3
répondu DigitalEye 2014-03-03 08:16:46

IMO, le diagramme ci-dessous donné dans ce article explique magnifiquement l'effet de déroulement de pile sur la route de l'instruction suivante (à exécuter une fois qu'une exception est lancée qui n'est pas tirée):

enter image description here

dans le pic:

  • d'en Haut est celui d'un appel normal d'exécution (avec aucune exception n'est levée).
  • en bas lorsqu'une exception est levée.

dans le second cas, lorsqu'une exception se produit, la pile d'appels de fonction est recherchée linéairement pour le gestionnaire d'exception. La recherche se termine à la fonction avec handler d'exception i.e. main() avec l'inclusion try-catch bloc, mais pas avant enlever toutes les entrées avant lui de la pile d'appel de fonction.

3
répondu Saurav Sahu 2017-09-01 09:57:41

Lorsqu'une exception est lancée et que le contrôle passe d'un bloc d'essai à un gestionnaire, le C++ run time appelle des destructeurs pour tous les objets automatiques construits depuis le début du bloc d'essai. Ce processus est appelé déroulage de pile. L'automatique objets sont détruits dans l'ordre inverse de leur construction. (Les objets automatiques sont des objets locaux qui ont été déclarés auto ou registre, ou pas déclaré statique ou externe. Un objet X automatique est supprimé à la sortie du programme. bloc dans lequel x est déclarée.)

si une exception est lancée lors de la construction d'un objet composé de sous-objets ou d'éléments de réseau, les destructeurs ne sont appelés que pour les sous-objets ou éléments de réseau construits avec succès avant que l'exception ne soit lancée. Un destructeur pour un objet statique sera appelée uniquement si l'objet a été construit avec succès.

1
répondu MK. 2010-04-16 10:47:10

En Java pile unwiding ou unwounding n'est pas très important (garbage collector). Dans beaucoup de papiers de traitement d'exception j'ai vu ce concept (stack unwinding), en particulier ceux qui écrivent traite du traitement d'exception en C ou C++. avec try catch blocs nous ne devrions pas oublier: pile libre de tous les objets après les blocs locaux .

1
répondu user2568330 2013-07-10 12:23:52