Lo stack di chiamate inizia in basso o in alto?


11

Una pila è qualcosa che si accumula dal basso verso l'alto.

Quindi uno stack di chiamate aggiunge nuovi elementi nello stack quando vengono chiamate funzioni con elementi che vengono rimossi dallo stack quando ciascuna funzione termina fino a quando lo stack è vuoto e quindi termina il programma.

Se quanto sopra è corretto, perché le persone si riferiscono al controllo spostando "in alto" lo stack di chiamate? Sicuramente il controllo si sposta verso il basso dello stack di chiamate fino a raggiungere il fondo.


Quando viene chiamata una funzione, un oggetto viene aggiunto nella parte superiore della pila e il controllo viene passato a quella funzione. Quindi il controllo si sposta dall'elemento sottostante nella pila all'elemento superiore di esso - verso l'alto.
treecoder,

1
@greengit: l'espressione "up the call stack" viene utilizzata con eccezioni, in cui il controllo si sposta effettivamente nel modo opposto.
Michael Borgwardt,

@MichaelBorgwardt: hai ragione.
treecoder

1
@MichaelBorgwardt: ho visto l'espressione "gli errori si muovono fino lo stack di chiamate". Sicuramente non è corretto.
CJ7,

Risposte:


9

Esistono due possibili motivi per questo utilizzo:

  • Nel contesto delle eccezioni, il controllo passa alla funzione / al metodo di chiamata e questa gerarchia di chiamata viene generalmente visualizzata con il metodo principale in alto e le chiamate di metodo formano una gerarchia verso il basso, con un livello decrescente di astrazioni. In questa gerarchia, un'eccezione si sposta verso l'alto.

  • Lo stack di programma effettivo in una normale applicazione x86 è invertito, cioè cresce verso il basso. Le istruzioni del codice macchina PUSH / PUSHW / PUSHD riducono il puntatore dello stack. Altre architetture possono condividere questo modello.


L'idea dall'alto verso il basso non è contraria al concetto quotidiano di "pila" che è un mucchio di oggetti che iniziano dal basso?
CJ7,

@CraigJ: così è il fatto che i bit di ciascun byte del contenuto dello stack verranno archiviati fisicamente in chip separati. Che importa?
Michael Borgwardt,

1

Tutto dipende dalla definizione delle parole; cosa intendi esattamente con le parole "top" e "bottom" in questo contesto, e anche sull'implementazione del sistema operativo o dell'architettura del computer.

Ricordo quanto segue da molto tempo fa, quando stavo programmando sul Commodore 64. La memoria tra l'indirizzo $ 0800 (2048) e $ 9FFF (40959) era riservata ai programmi BASIC. Il codice del tuo programma BASIC è stato memorizzato a partire dall'indirizzo inferiore ($ 0800, crescendo verso l'alto da lì). Lo stack, per l'archiviazione di variabili e indirizzi di ritorno di subroutine, è iniziato nella parte superiore ($ 9FFF) di quell'intervallo e si è sviluppato verso indirizzi più bassi. Quindi in questo contesto era logico vedere lo stack crescere verso il basso, e quando si ritorna da una subroutine il frame dello stack della subroutine è stato scartato incrementando il puntatore dello stack, in modo da poter dire che si stava "spostando verso l'alto lo stack" quando ritorno da una subroutine.

Non so come funzioni su versioni moderne, ad esempio processori Windows o Intel x86. Forse lo stack funziona al contrario, cioè cresce da indirizzi più bassi a più alti. Se così fosse, probabilmente useresti le parole "top", "bottom" e "up", "down" esattamente al contrario.


0

Per chiamare una funzione come foo (6, x + 1) ...

  1. Valuta le espressioni dei parametri effettive, come x + 1, nel contesto del chiamante.
  2. Allocare memoria per i locali di foo () spingendo un adeguato "blocco locale" di memoria su uno "stack di chiamate" di runtime dedicato a questo scopo. Per i parametri ma non le variabili locali, memorizzare i valori dal passaggio (1) nello slot appropriato nel blocco locale di foo ().
  3. Memorizza l'attuale indirizzo di esecuzione del chiamante (il suo "indirizzo di ritorno") e commuta l'esecuzione su foo ().
  4. foo () viene eseguito con il suo blocco locale comodamente disponibile alla fine dello stack di chiamate.
  5. Quando foo () è terminato, esce estraendo i suoi locali dallo stack e "ritorna" al chiamante utilizzando l'indirizzo di ritorno precedentemente memorizzato. Ora i locali del chiamante sono alla fine dello stack e possono riprendere l'esecuzione.

Riferimento:

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


Si noti che questo è altamente specifico per la convenzione di chiamata. Esistono convenzioni di chiamata che consentono al chiamante di ripulire, la maggior parte utilizza almeno alcuni registri, ecc.

0

Se concettualizzi una pila come una cosa dal basso verso l'alto, come un cilindro con dentro delle palline da tennis nella normale realtà gravitazionale, il controllo si sposta sulla pila come vengono chiamate le funzioni. Una volta completate le funzioni, il controllo si sposta verso il basso dello stack.

Se concettualizzi una pila come una cosa dall'alto verso il basso, come lo stesso cilindro di palline da tennis ma con gravità invertita, allora il controllo si sposta verso l'alto nella pila quando vengono chiamate le funzioni e verso l'alto nella pila quando le funzioni sono complete.

Questi sono entrambi solo modelli nella tua testa e sono essenzialmente totalmente arbitrari. Puoi concettualizzarlo come una cosa da un lato all'altro se preferisci, ma potresti avere difficoltà a comunicare con le persone. Personalmente penso che se A chiama B e B chiama C che C è il fondo dello stack (realtà gravitazionale invertita) e se si verifica un'eccezione in C, si desidera creare una bolla "fino" ad A. Penso che questo potrebbe essere il uso del linguaggio più comune perché la sensazione è che C sia in fondo e A in cima. La prima funzione è più intuitiva per me e le funzioni diventano più profonde ad ogni chiamata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.