Leçon
La théorie — La pile d’appels : cadres, adresses de retour et débordements
La pile d’appels est une zone de mémoire où chaque appel de fonction dispose d’un cadre. Celui-ci contient ici l’argument, l’adresse de retour, où la fonction appelante reprendra son exécution, le pointeur de cadre sauvegardé de cette fonction et les variables locales. Le pointeur de pile indique la plus basse adresse utilisée. Un appel le fait descendre de la taille d’un cadre ; un retour le fait remonter. Une récursion de profondeur d, avec des cadres de f octets, occupe d·f octets dans la pile.
Ce que signifie chaque symbole
d- la profondeur : le nombre de cadres présents simultanément dans la pile
f- la taille d’un cadre en octets : ici 12, plus 4 par variable locale
- Suppose
- Le modèle représente une machine 32 bits avec une convention d’appel classique du C : des emplacements de 4 octets, une pile qui croît vers les adresses basses, des arguments transmis par la pile et un pointeur de cadre conservé dans chaque cadre. Les conventions d’appel des machines 64 bits transmettent les premiers arguments dans des registres. Les compilateurs peuvent omettre le pointeur de cadre. Sur ARM, une fonction qui n’en appelle aucune autre peut conserver son adresse de retour dans un registre. La taille et l’organisation des cadres réels peuvent donc différer de ce modèle à trois emplacements.
Problème entièrement résolu
-
Jusqu’à quelle profondeur peut-on aller en récursion ? 4 étapes
Cliquez sur fact(11) déborde. La pile de cette page contient 128 octets. Par défaut, l’éditeur de liens de Microsoft réserve 1 Mo de pile à un programme. Prenez ici 1 Mio, soit 1 048 576 octets, avec les cadres de cette page. Quelle profondeur fact peut-elle atteindre ? Et si chaque appel possède en plus une variable locale de 4 octets ? Enfin, combien d’espace fib(30) occupe-t-elle dans la pile, pour combien d’appels ?
Ouvrez ce problème dans l'outil
-
Un cadre contient ici un argument, une adresse de retour et un pointeur de cadre sauvegardé : 3 × 4 = 12 octets. Comme 128 / 12 = 10,67, la pile peut accueillir 10 cadres. fact(11) exige 11 × 12 = 132 octets. Le tableau des résultats indique le débordement.
-
1 048 576 / 12 = 87 381,33 : la pile peut accueillir 87 381 cadres. fact pourrait donc atteindre 87 381 niveaux de récursion avant d’épuiser la pile. Son résultat dépasserait bien plus tôt la capacité d’un int de 32 bits, dès 13! = 6 227 020 800.
-
Avec une variable locale de 4 octets, chaque cadre occupe 16 octets. Or 1 048 576 / 16 = 65 536 exactement.
-
La plus longue chaîne de fib(30) contient 30 cadres, soit 30 × 12 = 360 octets. Le nombre d’appels vaut 2·F(31) − 1, où F(31) = 1 346 269 est le 31e nombre de Fibonacci. On obtient 2 692 537 appels.
Réponse
87 381 niveaux, ou 65 536 avec une variable locale. fib(30) utilise 360 octets de pile pour 2 692 537 appels. La profondeur détermine l’espace occupé dans la pile ; pour fib, le nombre d’appels détermine le temps d’exécution.
-
Parcours
Comment un ordinateur exécute un programme
Références (2)
- The worked problem's stack size: the linker's default reservation: Microsoft Learn, "/STACK (Stack allocations)," C/C++ build reference, updated 23 February 2022.
- The first insight's 1988 example: fingerd's unbounded read into a 512-byte buffer on the stack: Bob Page, "A Report on the Internet Worm." University of Lowell, 7 November 1988 (copy hosted at Toronto Metropolitan University).