En C++, un débordement de pile entraîne généralement un crash irrécupérable du programme. Pour les programmes qui doivent être vraiment robustes, 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.
Existe-t-il un moyen d'empêcher le débordement de pile par une technique générale. (Une solution évolutive et robuste, qui comprend le traitement des bibliothèques externes consommant beaucoup de pile, etc.)
Existe-t-il un moyen de gérer les débordements de pile au cas où ils se produiraient? De préférence, la pile est déroulée jusqu'à ce qu'il y ait un gestionnaire pour traiter ce genre de problème.
Il existe des langues qui ont des threads 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é.
La gestion d'un débordement de pile n'est pas la bonne solution, mais vous devez vous assurer que votre programme ne déborde pas de la pile.
N'allouez pas de grandes variables sur la pile (où ce qui est "grand" dépend du programme). Assurez-vous 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, gérez vous-même la récursivité (en conservant votre propre pile allouée dynamiquement) ou transformez l'algorithme récursif en un algorithme itératif équivalent
Un programme qui doit être "vraiment robuste" n'utilisera pas de bibliothèques tierces ou externes qui "consomment beaucoup de pile".
Notez que certaines plates-formes notifient un programme lorsqu'un débordement de pile se produit et permettent au programme de gérer l'erreur. Sous Windows, par exemple, une exception est levée. Cette exception n'est pas une exception C++, cependant, c'est une exception asynchrone. Alors qu'une exception C++ ne peut être levée que par une instruction throw
, une exception asynchrone peut être levée à tout moment pendant l'exécution d'un programme. Ceci est cependant attendu, car un débordement de pile peut survenir à tout moment: tout appel de fonction ou allocation de pile peut déborder de la pile.
Le problème est qu'un débordement de pile peut provoquer la levée d'une exception asynchrone même à partir de code qui ne devrait pas générer d'exceptions (par exemple, à partir de fonctions marquées noexcept
ou throw()
en C++). Donc, même si vous gérez cette exception d'une manière 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 une exception asynchrone est de ne pas la gérer du tout(*). Si l'un est lancé, 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 toutes ces méthodes sont susceptibles de souffrir du même problème: le code qui ne devrait pas provoquer d'erreur peut provoquer une erreur.
(*) Il existe quelques exceptions très rares.
Vous pouvez vous protéger contre les débordements de pile en utilisant de bonnes pratiques de programmation, comme:
ce sont les plus SO causes que j'ai vues ces dernières années.
Pour une recherche automatique SO, vous devriez pouvoir trouver des outils d'analyse de code statique.
Le C++ est un langage puissant, et avec ce pouvoir vient la capacité de se tirer une balle dans le pied. Je ne connais aucun mécanisme portable pour détecter et corriger/abandonner lorsqu'un débordement de pile se produit. Une telle détection serait certainement spécifique à l'implémentation. Par exemple, g ++ fournit -fstack-protector
pour vous 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 prudent avec les appels récursifs.
Re: piles 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 de l'assembleur de gcc.