Momentan studiere ich für meine Computerorganisation Midterm, und ich versuche, den Stapelzeiger und den Stapel vollständig zu verstehen. Ich kenne folgende Fakten, die das Konzept umgeben:
Und das Hinzufügen von etwas zum Stack erfolgt in zwei Schritten:
%Vor%Was mich daran hindert, vollständig zu verstehen, ist, dass ich mir keine relevante, selbstverständliche Situation einfallen lassen kann, in der ich Daten mit einem Stapelzeiger suchen und / oder verfolgen möchte.
Könnte jemand das Konzept als Ganzes näher erläutern und mir einige nützliche Codebeispiele geben?
Eine wichtige Verwendung von stack ist das Verschachteln von Subroutinenaufrufen.
Jede Subroutine kann eine Gruppe von Variablen lokal für diese Subroutine haben. Diese Variablen können bequem auf einem Stapel in einem Stapelrahmen gespeichert werden. Einige Aufrufkonventionen übergeben auch Argumente auf dem Stapel.
Die Verwendung von Unterprogrammen bedeutet auch, dass Sie den Anrufer im Auge behalten müssen, dh die Absenderadresse. Einige Architekturen haben einen dedizierten Stack für diesen Zweck, während andere implizit den "normalen" Stack verwenden. MIPS verwendet standardmäßig nur ein Register, aber in Nicht-Blatt-Funktionen (dh Funktionen, die andere Funktionen aufrufen) wird die Rückkehradresse überschrieben. Daher müssen Sie den ursprünglichen Wert speichern, normalerweise auf dem Stapel unter Ihren lokalen Variablen. Die Aufrufkonventionen können auch deklarieren, dass einige Registerwerte über Funktionsaufrufe hinweg erhalten bleiben müssen. Sie können sie in ähnlicher Weise mit dem Stack speichern und wiederherstellen.
Angenommen, Sie haben dieses C-Fragment:
%Vor%Die MIPS-Assembly sieht dann möglicherweise so aus:
%Vor% Die MIPS-Aufrufkonvention erfordert, dass die ersten vier Funktionsparameter in den Registern a0
bis a3
und der Rest, falls es mehr gibt, auf dem Stapel liegen. Außerdem muss der Funktionsaufrufer vier Plätze auf dem Stapel für die ersten vier Parameter reservieren, obwohl diese in den Registern übergeben werden.
Wenn Sie also auf Parameter fünf (und weitere Parameter) zugreifen möchten, müssen Sie sp
verwenden. Wenn die Funktion wiederum andere Funktionen aufruft und ihre Parameter nach den Aufrufen verwendet, muss sie a0
bis a3
in diesen vier Steckplätzen auf dem Stapel speichern, um zu verhindern, dass sie verloren gehen / überschrieben werden. Auch hier verwenden Sie sp
, um diese Register in den Stack zu schreiben.
Wenn Ihre Funktion lokale Variablen hat und nicht alle in Registern behalten kann (wie wenn sie a0
bis a3
nicht behalten kann, wenn sie andere Funktionen aufruft), muss sie den On-Stack verwenden Platz für diese lokalen Variablen, was wiederum die Verwendung von sp
erfordert.
Zum Beispiel, wenn Sie dies hatten:
%Vor%seine Demontage wäre so etwas wie:
%Vor% Siehe sp
, um auf x5
zuzugreifen.
Und dann, wenn Sie Code wie folgt haben:
%Vor%Dies ist, was es in der Disassembly nach der Kompilierung aussieht:
%Vor%Ich hoffe, ich habe den Code kommentiert, ohne Fehler zu machen.
Beachten Sie also, dass der Compiler in der Funktion r16
und r17
verwendet, sie jedoch auf dem Stapel speichert. Da die Funktion eine andere aufruft, muss sie auch ihre Rücksprungadresse auf dem Stapel behalten, anstatt sie einfach in r31
zu behalten.
PS Denken Sie daran, dass alle Verzweigungs- / Sprungbefehle in MIPS den unmittelbar folgenden Befehl ausführen, bevor die Steuerung tatsächlich an einen neuen Speicherort übertragen wird. Das könnte verwirrend sein.