Es ist nicht möglich, eine Funktionsaufrufsemantik zu implementieren, ohne einen Stapel zu verwenden. Es können nur Wortspiele gespielt werden (z. B. mit einem anderen Namen wie "FILO return buffer").
Es ist möglich, etwas zu verwenden, das keine Funktionsaufrufsemantik implementiert (z. B. Continuation-Passing-Stil, Akteure), und dann darüber Funktionsaufrufsemantik zu erstellen. Dies bedeutet jedoch, dass eine Art Datenstruktur hinzugefügt wird, um zu verfolgen, an welche Stelle die Steuerung übergeben wird, wenn die Funktion zurückkehrt, und dass diese Datenstruktur eine Art Stapel (oder ein Stapel mit einem anderen Namen / einer anderen Beschreibung) ist.
Stellen Sie sich vor, Sie haben viele Funktionen, die sich alle gegenseitig aufrufen können. Zur Laufzeit muss jede Funktion wissen, wohin sie beim Beenden der Funktion zurückkehren muss. Bei first
Anrufen second
haben Sie:
second returns to somewhere in first
Wenn Sie dann second
Anrufe third
haben:
third returns to somewhere in second
second returns to somewhere in first
Wenn Sie dann third
Anrufe fourth
haben:
fourth returns to somewhere in third
third returns to somewhere in second
second returns to somewhere in first
Wenn jede Funktion aufgerufen wird, müssen mehr Informationen zum "Zurückgeben" irgendwo gespeichert werden.
Wenn eine Funktion zurückgegeben wird, werden die Informationen zum Zurückgeben verwendet und nicht mehr benötigt. Wenn Sie zum Beispiel zu einem Ort fourth
zurückkehren, an dem Sie sich befinden, third
wird die Menge der Informationen, zu denen Sie zurückkehren möchten, wie folgt:
third returns to somewhere in second
second returns to somewhere in first
Grundsätzlich gilt; "Funktionsaufrufsemantik" impliziert Folgendes:
- Sie müssen Informationen darüber haben, wohin Sie zurückkehren können
- Die Informationsmenge wächst, wenn Funktionen aufgerufen werden, und verringert sich, wenn Funktionen zurückkehren
- Der erste Teil der gespeicherten "Rückgabeort" -Information ist der letzte Teil der verworfenen "Rückgabeort" -Information
Dies beschreibt einen FILO / LIFO-Puffer oder einen Stapel.
Wenn Sie versuchen, einen Baumtyp zu verwenden, hat jeder Knoten im Baum nie mehr als ein untergeordnetes Element. Hinweis: Ein Knoten mit mehreren untergeordneten Knoten kann nur dann auftreten, wenn eine Funktion zwei oder mehr Funktionen gleichzeitig aufruft. Dies erfordert eine gewisse Parallelität (z. B. Threads, Fork () usw.) und wäre keine "Funktionsaufrufsemantik". Wenn jeder Knoten in der Baumstruktur niemals mehr als ein Kind haben wird; dann würde dieser "Baum" nur als ein FILO / LIFO-Puffer oder ein Stapel verwendet werden; und weil es nur als FILO / LIFO-Puffer oder Stapel verwendet wird, kann man mit Recht behaupten, dass der "Baum" ein Stapel ist (und der einzige Unterschied sind Wortspiele und / oder Implementierungsdetails).
Dasselbe gilt für jede andere Datenstruktur, die möglicherweise zur Implementierung der "Funktionsaufrufsemantik" verwendet wird. Sie wird als Stapel verwendet (und der einzige Unterschied besteht in Wortspielen und / oder Implementierungsdetails). es sei denn, es unterbricht die "Funktionsaufrufsemantik". Hinweis: Wenn möglich, würde ich Beispiele für andere Datenstrukturen bereitstellen, mir fällt jedoch keine andere Struktur ein, die leicht plausibel ist.
Natürlich ist die Implementierung eines Stacks ein Implementierungsdetail. Dies kann ein Speicherbereich sein (in dem Sie den Überblick über einen "aktuellen Stapel" behalten), eine Art verknüpfte Liste (in dem Sie den Überblick über einen "aktuellen Eintrag in der Liste" behalten) oder eine Implementierung in einigen andere Weise. Es spielt auch keine Rolle, ob die Hardware eine integrierte Unterstützung hat oder nicht.
Anmerkung: Wenn zu einem Zeitpunkt nur ein Aufruf einer Prozedur aktiv sein darf. Anschließend können Sie statisch Speicherplatz für Informationen zum Zurückkehren zuweisen. Dies ist immer noch ein Stapel (z. B. eine verknüpfte Liste von statisch zugewiesenen Einträgen, die auf FILO / LIFO-Weise verwendet werden).
Beachten Sie auch, dass es einige Dinge gibt, die nicht der "Funktionsaufrufsemantik" folgen. Zu diesen Dingen gehören "möglicherweise sehr unterschiedliche Semantiken" (z. B. Continuation Passing, Actor Model); und enthält auch allgemeine Erweiterungen für "Funktionsaufrufsemantik" wie Parallelität (Threads, Fasern, was auch immer), setjmp
/ longjmp
, Ausnahmebehandlung usw.