Non è possibile implementare la semantica della chiamata di funzione senza utilizzare una sorta di stack. È possibile giocare solo a giochi di parole (ad es. Usare un nome diverso, come "Buffo return buffer").
È possibile utilizzare qualcosa che non implementa la semantica di chiamata di funzione (ad es. Stile di passaggio di continuazione, attori) e quindi costruire sopra di essa una semantica di chiamata di funzione; ma ciò significa aggiungere una sorta di struttura di dati per tracciare dove viene passato il controllo quando la funzione ritorna, e quella struttura di dati sarebbe un tipo di stack (o uno stack con un nome / una descrizione diversi).
Immagina di avere molte funzioni che possono chiamarsi a vicenda. In fase di esecuzione, ogni funzione deve sapere dove tornare quando esce dalla funzione. Se first
chiama second
allora hai:
second returns to somewhere in first
Quindi, se hai delle second
chiamate third
:
third returns to somewhere in second
second returns to somewhere in first
Quindi, se hai delle third
chiamate fourth
:
fourth returns to somewhere in third
third returns to somewhere in second
second returns to somewhere in first
Come viene chiamata ogni funzione, più informazioni "dove restituire" devono essere archiviate da qualche parte.
Se una funzione restituisce, vengono utilizzate le informazioni "dove restituire" e non sono più necessarie. Ad esempio, se fourth
ritorna da qualche parte, third
la quantità di informazioni "dove tornare" diventerebbe:
third returns to somewhere in second
second returns to somewhere in first
Fondamentalmente; "semantica di chiamata di funzione" implica che:
- è necessario disporre delle informazioni "dove restituire"
- la quantità di informazioni aumenta quando vengono chiamate le funzioni e si riduce quando le funzioni ritornano
- il primo pezzo di informazioni "dove restituire" memorizzato sarà l'ultimo pezzo di informazioni "dove restituire" scartato
Descrive un buffer FILO / LIFO o uno stack.
Se si tenta di utilizzare un tipo di albero, tutti i nodi dell'albero non avranno mai più di un figlio. Nota: un nodo con più figli può verificarsi solo se una funzione chiama 2 o più funzioni contemporaneamente , il che richiede una sorta di concorrenza (ad esempio thread, fork (), ecc.) E non sarebbe "semantica di chiamata di funzione". Se ogni nodo dell'albero non avrà mai più di un figlio; quindi quell'albero sarebbe usato solo come buffer FILO / LIFO o stack; e poiché è usato solo come buffer FILO / LIFO o stack è corretto affermare che "albero" è uno stack (e l'unica differenza sono i giochi di parole e / o i dettagli di implementazione).
Lo stesso vale per qualsiasi altra struttura di dati che potrebbe essere plausibilmente utilizzata per implementare la "semantica della chiamata di funzione" - sarà usata come una pila (e l'unica differenza sono i giochi di parole e / oi dettagli di implementazione); a meno che non interrompa la "semantica della funzione chiamata". Nota: fornirei esempi per altre strutture di dati se potessi, ma non riesco a pensare a nessuna altra struttura leggermente plausibile.
Naturalmente come viene implementato uno stack è un dettaglio di implementazione. Potrebbe essere un'area di memoria (in cui si tiene traccia di uno "stack top corrente"), potrebbe essere una sorta di elenco collegato (in cui si tiene traccia di "voce corrente nell'elenco"), oppure potrebbe essere implementato in alcuni altro modo. Inoltre, non importa se l'hardware ha il supporto integrato o meno.
Nota: se una sola invocazione di una procedura può essere attiva in qualsiasi momento; quindi è possibile allocare staticamente lo spazio per le informazioni "dove tornare". Questo è ancora uno stack (ad esempio un elenco collegato di voci allocate staticamente utilizzate in modo FILO / LIFO).
Si noti inoltre che ci sono alcune cose che non seguono la "semantica della chiamata di funzione". Queste cose includono "semantica potenzialmente molto diversa" (ad es. Continuazione che passa, modello dell'attore); e include anche estensioni comuni a "semantica di chiamate di funzioni" come concorrenza (thread, fibre, qualunque cosa), setjmp
/ longjmp
, gestione delle eccezioni, ecc.