Stack • In der Informatik bezeichnet ein Stack eine häufig eingesetzte Datenstruktur. • Sie wird von den meisten Mikroprozessoren in Hardware direkt unterstützt. • Ein Stack wird manchmal auch als Stapelspeicher oder Kellerspeicher (kurz Stapel oder Keller) bezeichnet. 30.03.2006 Dr. Jörg Gruner 1 Stack • Funktionsprinzip – Ein Stapel kann eine (definierte) Menge von Objekten aufnehmen und gibt diese entgegengesetzt zur Reihenfolge der Aufnahme wieder zurück. – Dabei wird nach dem Last In – First Out – Prinzip • deutsch: zuletzt hinein und zuerst heraus • LIFO gearbeitet, das heißt es wird immer das Objekt aus dem Stapel zurückgegeben, welches als letztes hineingelegt wurde. – Nicht zu verwechseln mit einer Warteschlange (First In – First Out – Prinzip). 30.03.2006 Dr. Jörg Gruner 2 Stack • Funktionsprinzip – Ein Stapelspeicher ist mit einen Stapel von Umzugskisten vergleichbar. – Es kann immer eine neue Kiste oben auf den Stapel gepackt werden oder eine Kiste von oben heruntergenommen werden. – Der Zugriff ist immer nur auf das oberste Element des Stapels möglich. – Ein Hinzufügen oder Entfernen einer Kiste weiter unten im Stapel ist nicht möglich. 30.03.2006 Dr. Jörg Gruner 3 Stack • Funktionsprinzip – Beim Stack spricht man von automatischer Speicheranforderung. – Die Laufzeitkosten einer automatischen Speicheranforderung sind in der Regel deutlich geringer als die bei der dynamischen Speicheranforderung (Heap). – Allerdings ist bei intensiver Nutzung durch sehr große oder sehr viele Anforderungen der für den Stack reservierte Speicher bald aufgebraucht. – In diesem Fall droht ein Programmabbruch wegen Stapelüberlauf (Stack-Overflow). 30.03.2006 Dr. Jörg Gruner 4 Stack • Funktionsprinzip – Stapel stellen die Operationen • push (einkellern) zum Hinzufügen eines Objektes und • pop (auskellern) zum Zurückholen und Entfernen eines Objektes bereit. Außerdem steht oft der Operator • peek (nachsehen) zum Holen eines Objektes ohne es zu entfernen zur Verfügung. 30.03.2006 Dr. Jörg Gruner 5 Stack 30.03.2006 Dr. Jörg Gruner 6 Stack • Anwendungen – Mikroprozessoren • In Mikroprozessoren gibt es oft ein spezielles Register, den Stackpointer (Stapelzeiger). • Dieses Register enthält eine Speicheradresse, die auf den obersten Stapeleintrag zeigt. • Wenn mit der Operation push ein weiteres Objekt auf dem Stapel abgelegt wird, erhöht sich der Wert des Stapelzeigers und zeigt so auf die nächste Adresse, in die ein weiterer neuer Stapeleintrag geschrieben wird. • Bei pop wird der Eintrag der Adresse gelesen und anschließend der Stapelzeiger vermindert, so dass er auf den letzten Stapeleintrag zeigt. • Der Stackpointer kann im Allgemeinen auch direkt gesetzt werden. • In manchen Prozessoren wird der Stapelzeiger bei push vermindert und bei pop erhöht. Am Prinzip ändert dies aber nichts. 30.03.2006 Dr. Jörg Gruner 7 Stack • Anwendungen – Mikroprozessoren • Der Stapel des Mikroprozessors wird oft von diesem selbst bei Aufruf und Rücksprung von Unterprogrammen zur Speicherung der Rücksprungadresse genutzt, ohne dass ein zusätzliches push oder pop zum Ablegen oder Holen dieser Rücksprungadresse nötig ist, da die entsprechenden Anweisungen das Register selbst richtig setzen. • In Multitasking-Systemen gibt es für jedes Programm einen eigenen Stapelspeicher. Beim Umschalten zwischen Prozessen wird dieser durch direktes Setzen des Stapelzeigers initialisiert. 30.03.2006 Dr. Jörg Gruner 8 Stack • Anwendungen – Programmiersprachen • Compiler für moderne Programmiersprachen nutzen gewöhnlich push- und pop-Operationen vor dem Aufruf eines Unterprogramms, um an dieses Parameter zu übergeben. • Ähnlich können so auch Ergebnisse eines Unterprogrammes zurückgegeben werden. • Lokale Variablen, zum Beispiel von Unterprogrammen, können ebenfalls auf dem Stapel gespeichert werden. • Für die rekursive Programmierung wird dieser Mechanismus erweitert, da für die lokalen Variablen der Unterprogramme gegebenenfalls enormer Platz benötigt wird. Anderenfalls besteht die Gefahr des Stack-Overflow. 30.03.2006 Dr. Jörg Gruner 9 Stack • Anwendungen – Compiler • Zur Übersetzung des Quellcodes einer Formalen Sprache nutzen Compiler und Interpreter einen Parser, der bei der Textanalyse Syntax-Regeln auf einem Stapel ablegt und so vergleichend dem nachfolgenden Textelement eine angenommene Bedeutung (das oberste Stapelelement) zuordnen kann. • Die Verwendung eines Stapelspeichers zur Übersetzung von Programmiersprachen wurde 1957 von Friedrich Ludwig Bauer und Klaus Samelson unter dem Namen "Kellerprinzip" patentiert. Die internationale Anerkennung und Ehrung ihrer Leistung erfolgte erst im Jahre 1988. • Programmiersprachen, die auf einer virtuelle Maschine aufsetzen (zum Beispiel Java, C#), optimieren den kompilierten Zwischencode für die Verwendung eines Stapels, um zur Laufzeit die Interpretation dieses Zwischencodes zu beschleunigen. 30.03.2006 Dr. Jörg Gruner 10 Stack • Anwendungen – Verarbeitung von Klammerstrukturen • Stapelspeicher eignen sich auch zur Auswertung von Klammerausdrücken, wie sie etwa in der Mathematik geläufig sind. – Dabei wird zunächst für Operatoren und Operanden je ein Stapelspeicher initialisiert. – Der zu verarbeitende Klammerausdruck wird nun symbolweise eingelesen. – Wird eine öffnende Klammer eingelesen, so ist diese zu ignorieren. – Wird ein Operand oder Operator eingelesen, so ist dieser auf den jeweiligen Stapelspeicher zu legen. – Wird eine schließende Klammer eingelesen, so wir der oberste Operator vom Stapelspeicher für die Operatoren genommen und entsprechend diesem Operator eine geeignete Anzahl von Operanden, die zur Durchführung der Operation benötigt werden. – Das Ergebnis wird dann wieder auf dem Stapelspeicher für Operanden abgelegt. – Sobald der Stapelspeicher für die Operatoren leer ist, befindet sich im Stapelspeicher für die Operanden das Ergebnis. 30.03.2006 Dr. Jörg Gruner 11 Stack • Anwendungen – Postfixnotation • Zur Berechnung von Termen wird gelegentlich die Postfixnotation verwendet, die mit Hilfe der Operationen eine Klammersetzung und Prioritätsregeln für die Operationen überflüssig macht. • Zahlwerte werden automatisch auf dem Stapel abgelegt. – binäre Operatoren (zum Beispiel +, −, *, /) holen die oberen beiden Werte – unäre Operatoren (zum Beispiel Vorzeichenwechel) einen Wert vom Stapel und – legen anschließend das (Zwischen-)Ergebnis dort wieder ab. 30.03.2006 Dr. Jörg Gruner 12 Stack • Anwendungen – Infixnotation • Bei der maschinengestützten Auflösung von arithmetischen Ausdrücken in der so genannten Infixnotation – der Operator steht zwischen den beteiligten Zahlwerten werden zunächst vorrangige Teilterme in einem Stapel zwischengelagert und so faktisch der Infix-Term schrittweise in einen Postfix-Term umgewandelt, bevor das Ergebnis durch Abarbeiten des Stapels errechnet wird. 30.03.2006 Dr. Jörg Gruner 13 Stack • Anwendungen – Stapelorientierte Sprachen • Stapelorientierte Sprachen (z. B. Forth oder Postscript) wickeln fast alle Variablen-Operationen über einen Stapel ab und stellen neben den oben genannten Operatoren noch weitere zur Verfügung. Beispielsweise tauscht der Forth-Operator swap die obersten beiden Elemente des Stapels. Arithmetische Operationen werden in der Postfix-Notation aufgeschrieben und beeinflussen damit ebenfalls den Stapel. • Forth benutzte einen zweiten Stapel (Return-Stapel) zur Zwischenspeicherung der Rücksprungadressen von Unterprogrammen während der Ausführungsphase. Dieser Stapel wird auch während der Übersetzungsphase für die Adressen der Sprungziele für die Kontrollstrukturen verwendet. Die Übergabe und Rückgabe von Werten an Unterprogrammen geschieht über den ersten Stapel, der zweite nimmt die Rücksprungadresse auf. 30.03.2006 Dr. Jörg Gruner 14 Heap • In der Informatik bezeichnet der Heap – engl. Halde oder Haufen einen Speicherbereich. • Programme fordern zur Laufzeit zusammenhängende Speicherabschnitte aus dem Heap an und geben diese in beliebiger Reihenfolge wieder frei. • Deshalb wird der Heap auch als dynamischer Speicherbereich oder als Freispeicher bezeichnet. • Die Freigabe kann sowohl manuell als auch mit Hilfe einer automatischen Speicherbereinigung (Garbage-Collection) erfolgen. • Eine Speicheranforderung vom Heap bzw. Freispeicher wird auch dynamische Speicheranforderung genannt. • Der Heap wird im Unterschied zum Stack von der Hardware nicht unterstützt. 30.03.2006 Dr. Jörg Gruner 15 Heap • Unterstützung von dynamischen Speicheranforderungen in Programmiersprachen – Programmiersprachen unterstützen die dynamische Speicheranforderung auf unterschiedliche Weisen. • In ISO-C gibt es dafür beispielsweise die Funktion malloc(). • Mit der Funktion free() wird der Speicher wieder freigegeben. • In ISO-C++ gibt es weiterhin die Möglichkeit, Speicher dynamisch mit Hilfe von new() anzufordern und mit delete() wieder freizugeben. 30.03.2006 Dr. Jörg Gruner 16 Heap • Verwaltung des Heap durch die Laufzeitumgebung bzw. das Betriebssystem – siehe Kapitel „Speicherverwaltung“ der Vorlesung 30.03.2006 Dr. Jörg Gruner 17