Comment gérer ou éviter un débordement de pile en C++

en C++ un débordement de la pile conduit généralement à un plantage irrécupérable du programme. Pour les programmes qui doivent être vraiment robuste, c'est un comportement inacceptable, en particulier parce que la taille de la pile est limitée. Quelques questions sur la façon de gérer le problème.

  1. Est-il possible d'empêcher un débordement de pile par une technique générale. (Une solution évolutive, robuste, qui comprend traiter avec les bibliothèques externes manger beaucoup de, etc.)

  2. y a-t-il un moyen de gérer les débordements de piles au cas où ils se produiraient? De préférence, la pile se détend jusqu'à ce qu'il y ait un gestionnaire pour s'occuper de ce genre de problème.

  3. il y a des langues là-bas, qui ont des fils avec des piles extensibles. Est - ce que quelque chose comme ça est possible en C++?

tout autre commentaire utile sur la solution du comportement C++ serait apprécié.

22
demandé sur Ralph Tandetzky 2012-08-27 20:57:02

5 réponses

gérer un débordement de pile n'est pas la bonne solution, vous devez plutôt vous assurer que votre programme ne déborde pas la pile.

Ne pas allouer de grandes variables sur la pile (où ce qui est "grand" dépend du programme). S'assurer que tout algorithme récursif se termine après une profondeur maximale connue. Si un algorithme récursif peut récurser un nombre inconnu de fois ou un grand nombre de fois, soit gérer la récursion vous-même (en maintenant votre propre dynamiquement alloué stack) ou transformer l'algorithme récursif en un algorithme itératif équivalent

un programme qui doit être " vraiment robuste "n'utilisera pas de bibliothèques externes ou tierces qui" mangent beaucoup de piles."


notez que certaines plateformes notifient un programme lorsqu'un débordement de pile se produit et permettent au programme de gérer l'erreur. Sur les fenêtres, par exemple, une exception est lancée. Cette exception n'est pas une exception C++, mais une exception asynchrone. Alors qu'une exception C++ ne peut être lancée que par un throw déclaration, asynchrone exception peut être levée à tout moment durant l'exécution d'un programme. Cela est toutefois prévu parce qu'un débordement de la pile peut se produire à tout moment: tout appel de fonction ou allocation de la pile peut déborder la pile.

le problème est qu'un débordement de pile peut provoquer une exception asynchrone à être lancée, même à partir d'un code qui n'est pas prévu de lancer des exceptions (par exemple, à partir de fonctions marquées noexcept ou throw() EN c++). Donc, même si vous gérez cette exception d'une façon ou d'une autre, vous n'avez aucun moyen de savoir que votre programme est dans un état sûr. Par conséquent, la meilleure façon de gérer asynchrone exception n'est pas à traiter à tous (*). Si l'on est jeté, cela signifie que le programme contient un bogue.

D'autres plates-formes peuvent avoir des méthodes similaires pour "gérer" une erreur de débordement de pile, mais n'importe laquelle de ces méthodes est susceptible de souffrir du même problème: code qui est censé ne pas causer une erreur peut provoquer une erreur.

(*) il y a quelques rares exceptions.

22
répondu James McNellis 2012-08-27 17:38:36

vous pouvez vous protéger contre les débordements de pile en utilisant de bonnes pratiques de programmation, comme:

  1. soyez très prudent avec la récursion, j'ai récemment vu un SO résultant de la fonction de CreateDirectory récursive mal écrite, si vous n'êtes pas sûr que votre code est correct à 100%, alors ajoutez une variable de garde qui arrêtera l'exécution après N appels récursifs. Ou encore mieux, n'écrivez pas de fonctions récursives.
  2. ne créez pas de tableaux énormes sur la pile, cela pourrait être des tableaux cachés comme un très grand tableau comme champ de classe. Il est toujours préférable d'utiliser de vecteur.
  3. être très prudent avec alloca, surtout si elle est mise dans une macro définition. J'ai vu si nombreux résultant de macro de conversion de chaîne de caractères mis dans pour des boucles qui utilisaient alloca pour des allocations de mémoire rapides.
  4. assurez-vous que la taille de votre pile est optimale, ceci est plus important dans les plateformes embededées. Si vous thread ne fait pas beaucoup, alors donnez - lui une petite pile, sinon utilisez plus grand. Je sais réservation quelques adresse de gamme - pas de mémoire physique.

ce sont les causes les plus fréquentes que j'ai vues ces dernières années.

pour la recherche automatique ainsi vous devriez être en mesure de trouver des outils d'analyse statique de code.

6
répondu marcinj 2012-08-27 17:42:21

C++ est un langage puissant, et avec ce pouvoir vient la capacité de se tirer dans le pied. Je ne suis pas au courant d'un mécanisme portable pour détecter et corriger/Annuler lorsque le débordement de la pile se produit. Certainement une telle détection serait mise en œuvre spécifiques. Par exemple g++ fournit -fstack-protector pour aider à surveiller l'utilisation de votre pile.

en général, votre meilleur pari est d'être proactif en évitant les grandes variables basées sur la pile et en faisant attention avec les appels récursifs.

2
répondu Mark B 2012-08-27 17:20:48

Re: stacks extensibles. Vous pourriez vous donner plus d'espace de pile avec quelque chose comme ceci:

#include <iostream>

int main()
{
    int sp=0;

    // you probably want this a lot larger
    int *mystack = new int[64*1024];
    int *top = (mystack + 64*1024);

    // Save SP and set SP to our newly created
    // stack frame
    __asm__ ( 
        "mov %%esp,%%eax; mov %%ebx,%%esp":
        "=a"(sp)
        :"b"(top)
        :
        );
    std::cout << "sp=" << sp << std::endl;

    // call bad code here

    // restore old SP so we can return to OS
    __asm__(
        "mov %%eax,%%esp":
        :
        "a"(sp)
        :);

    std::cout << "Done." << std::endl;

    delete [] mystack;
    return 0;
}

c'est la syntaxe assembleur de gcc.

1
répondu Nathan Andrew Mullenax 2012-08-27 18:02:33
#include <iostream>
using **namespace** std;
class Complex
{

 public: double *re, *im;

 Complex()
 {

    re = new double(r);

    im = new double(m);

}

Complex( )
{

    re = new double; 
    im = new double;
    *re = *t.re; 
    *im= *t.im;

}

~Complex()
{

        delete re, im;

}

};

int main() {

double x, y, z;
cin >> x >> y  >> z;
Complex n1(x,y);
cout << *n1.re << "+" << *n1.im << "i ";
Complex n2 = n1;
cout << *n2.re << "+" << *n2.im << "i ";
*n1.im = z;
cout << *n2.re << "+" << *n2.im << "i ";
cout << *n1.re << "+" << *n1.im << "i ";
return 0;
}
-2
répondu suman das 2017-03-15 06:02:27