Le dépassement de pile (stack overflow) est une situation dans laquelle un programme consomme plus de mémoire de pile que ce qui a été alloué par le système. Historiquement, la pile était utilisée pour stocker des variables locales, des adresses de retour et des données temporaires lors des appels de fonctions. Dans les premières implémentations de C, la pile était assez petite et non protégée contre les dépassements.
Le problème survient lorsque qu'une fonction ou une chaîne d'appels de fonctions utilise trop de variables locales ou appelle des fonctions récursives sans condition de sortie, provoquant ainsi l'écriture de données en dehors de la mémoire allouée à la pile, entraînant des erreurs, des pannes et des vulnérabilités.
La solution consiste à concevoir des fonctions économes en mémoire, à éviter les récursions profondes ou infinies, et à ne pas placer de grands objets sur la pile. Le système d'exploitation peut prévenir les débordements grâce à une protection par pages de garde (guard pages), mais le développeur doit écrire du code qui n'entraîne pas de dépassements.
Exemple de code provoquant un dépassement de pile en raison d'une récursion infinie :
void foo() { int arr[1000]; // Un grand tableau local ne fait qu'aggraver le problème foo(); // Appel récursif sans sortie } int main() { foo(); return 0; }
Caractéristiques clés :
Que se passe-t-il si l'on déclare un très grand tableau local dans une fonction (par exemple, int arr[1000000]) ?
Réponse : Un grand tableau local peut immédiatement utiliser toute la pile. En fonction du système d'exploitation et du compilateur, cela entraînera un échec lors de l'exécution de la fonction ou même un plantage du programme.
Exemple de code :
void func() { int arr[1000000]; // Beaucoup de mémoire arr[0] = 1; }
La récursion entraîne-t-elle toujours un débordement de pile ?
Réponse : Non, la récursion est utile si elle est limitée en profondeur. Le dépassement se produit uniquement si la profondeur de la récursion est élevée ou si elle n'est pas limitée.
Peut-on placer de grands tableaux statiques à l'intérieur des fonctions pour économiser de la mémoire ?
Réponse : Non, les grands tableaux static à l'intérieur d'une fonction occupent quand même de la mémoire, mais déjà dans le segment des données statiques, et non sur la pile. Ce n'est pas toujours économique, surtout si une mémoire locale temporaire est requise.
Exemple de code :
void func() { static int arr[1000000]; // Pas sur la pile, mais la zone statique occupe de l'espace indéfiniment }
Un programmeur a implémenté un tri rapide via récursion pour trier un grand tableau, sans limiter la profondeur des appels et sans utiliser de conditions de sortie. Le code a conduit à un dépassement de pile lors du traitement de données réelles.
Avantages :
Inconvénients :
Un autre programmeur a utilisé une implémentation itérative avec sa propre petite pile dans le tas, a contrôlé la profondeur de la récursion et a alloué de grands tableaux temporaires via malloc.
Avantages :
Inconvénients :