La pile d'appels commence en bas ou en haut?

11

Une pile est quelque chose qui s'entasse de bas en haut.

Par conséquent, une pile d'appels ajoute de nouveaux éléments sur la pile lorsque des fonctions sont appelées avec des éléments retirés de la pile à la fin de chaque fonction jusqu'à ce que la pile soit vide, puis le programme se termine.

Si ce qui précède est correct, pourquoi les gens se réfèrent-ils au contrôle de déplacer "vers le haut" la pile d'appels? Le contrôle descend sûrement dans la pile des appels jusqu'à ce qu'il atteigne le bas.

CJ7
la source
Lorsqu'une fonction est appelée, un élément est ajouté en haut de la pile et le contrôle est passé à cette fonction. Par conséquent, le contrôle se déplace de l'élément sous-jacent dans la pile vers l'élément supérieur de celui-ci - vers le haut.
treecoder
1
@greengit: l'expression "up the call stack" est utilisée avec des exceptions, où le contrôle se déplace en fait dans le sens inverse.
Michael Borgwardt,
@MichaelBorgwardt: Vous avez raison.
treecoder
1
@MichaelBorgwardt: J'ai vu l'expression "les erreurs remontent la pile des appels". C'est sûrement incorrect.
CJ7

Réponses:

9

Il y a deux raisons possibles à cette utilisation:

  • Dans le contexte des exceptions, le contrôle se déplace vers la fonction / méthode appelante, et cette hiérarchie d'appels est généralement visualisée avec la méthode principale en haut et les appels de méthode formant une hiérarchie vers le bas, avec un niveau d'abstractions décroissant. Dans cette hiérarchie, une exception monte.

  • La pile de programme réelle dans une application x86 normale est inversée, c'est-à-dire qu'elle croît vers le bas. Les instructions de code machine PUSH / PUSHW / PUSHD diminuent le pointeur de pile. D'autres architectures peuvent partager ce modèle.

Michael Borgwardt
la source
L'idée descendante n'est-elle pas contraire au concept quotidien d'une «pile» qui est une pile d'articles commençant par le bas?
CJ7
@CraigJ: il en va de même du fait que les bits de chaque octet du contenu de la pile seront physiquement stockés dans des puces distinctes. On s'en fout?
Michael Borgwardt
1

Tout dépend de la définition des mots; que voulez-vous dire exactement avec les mots "haut" et "bas" dans ce contexte, ainsi que sur la mise en œuvre du système d'exploitation ou de l'architecture informatique.

Je me souviens de ce qui s'est passé il y a longtemps, lorsque je programmais sur le Commodore 64. La mémoire entre l'adresse $ 0800 (2048) et $ 9FFF (40959) était réservée aux programmes BASIC. Le code de votre programme BASIC a été stocké à partir de l'adresse inférieure (0800 $, en augmentant à partir de là). La pile, pour stocker les variables et les adresses de retour des sous-programmes, a commencé en haut (9FFF $) de cette plage et s'est développée vers des adresses inférieures. Donc, dans ce contexte, il était logique de voir la pile se développer vers le bas, et lorsque vous revenez d'un sous-programme, le cadre de pile du sous-programme a été rejeté en incrémentant le pointeur de la pile, de sorte que vous pouviez dire que vous "montiez la pile" lorsque revenant d'un sous-programme.

Je ne sais pas comment cela fonctionne sur les versions modernes des processeurs Windows ou Intel x86 par exemple. Peut-être que la pile fonctionne dans l'autre sens, c'est-à-dire qu'elle passe des adresses inférieures aux adresses supérieures. Si tel était le cas, vous utiliseriez probablement les mots "haut", "bas" et "haut", "bas" exactement dans l'autre sens.

Jesper
la source
0

Pour appeler une fonction telle que foo (6, x + 1) ...

  1. Évaluez les expressions de paramètre réelles, telles que x + 1, dans le contexte de l'appelant.
  2. Allouez de la mémoire aux sections locales de foo () en poussant un "bloc local" de mémoire approprié sur une "pile d'appel" d'exécution dédiée à cet effet. Pour les paramètres mais pas les variables locales, stockez les valeurs de l'étape (1) dans l'emplacement approprié dans le bloc local de foo ().
  3. Stockez l'adresse d'exécution actuelle de l'appelant (son "adresse de retour") et basculez l'exécution sur foo ().
  4. foo () s'exécute avec son bloc local facilement accessible à la fin de la pile d'appels.
  5. Lorsque foo () est terminé, il quitte en sortant ses sections locales de la pile et "retourne" à l'appelant en utilisant l'adresse de retour précédemment stockée. Maintenant, les sections locales de l'appelant sont à la fin de la pile et il peut reprendre son exécution.

Référence:

http://cslibrary.stanford.edu/102/PointersAndMemory.pdf (p15)

CodeART
la source
Notez que cela est très spécifique à la convention d'appel. Il existe des conventions d'appel qui permettent à l'appelant de nettoyer, la plupart utilisent au moins certains registres, etc.
0

Si vous conceptualisez une pile comme une chose ascendante, comme un cylindre avec des balles de tennis dans la réalité gravitationnelle normale, le contrôle monte la pile lorsque les fonctions sont appelées. Une fois les fonctions terminées, le contrôle descend la pile.

Si vous conceptualisez une pile comme une chose de haut en bas, comme le même cylindre de balles de tennis mais avec une gravité inversée, le contrôle se déplace vers le haut de la pile lorsque les fonctions sont appelées et vers le haut de la pile lorsque les fonctions sont terminées.

Ce ne sont que des modèles dans votre tête et sont essentiellement totalement arbitraires. Vous pouvez le conceptualiser comme une chose côte à côte si vous préférez, mais peut avoir du mal à communiquer avec les gens. Personnellement, je pense que si A appelle B et B appelle C, C est le bas de la pile (réalité gravitationnelle inversée) et si une exception se produit en C, vous voulez propulser cette exception "jusqu'à" à A. Je pense que cela peut être le utilisation du langage plus courante car le sentiment est que C est au fond et A est le sommet. La première fonction est plus intuitive pour moi et les fonctions s'approfondissent à chaque appel.

Ken Falk
la source