Meine Fragen zum Stack-Tutorial:
C ist sehr maschinennah und verwendet daher die gleichen Datenstruktur wie Assembler und damit der Prozessor: einen Stack.
Assembler ist eine Programmiersprache, Prozessor aber eine Hardware. Welcher Zusammenhang besteht zwischen den beiden (Assembler und Prozessor) und dem Stack? Wie sieht überhaupt eine Datenstruktur aus? Welche Aufgaben hat diese und wie arbeitet sie?
Die meisten Prozessoren besitzen ein eigenes Register für den Stack.
Was ist ein Register, wie funktioniert es und wofür brauche ich das für den Stack?
Wie schon beschrieben organisieren viele Programmiersprachen die Ablaufsteuerung über einen Stack. Zu organisieren sind Funktionsaufrufe, deren Parameter und Rückgaben, sowie lokale Variablen, innerhalb einer Funktion definiert werden. Beim Funktionsaufruf werden die Parameter und die Information, wo die Funktion überhaupt gerufen wird, auf eine Notiz geschrieben, damit man am Ende der Funktion an diese Stelle zurückspringen kann. Da am Ende jeder Funktion zur rufenden Funktion zurückgesprungen werden muss und man innerhalb von Funktionen auch andere Funktionen rufen kann, muss hier ein Stapel angelegt werden, wobei auf der obersten Notiz steht, wohin ich zurückspringen muss.
Warum reichen die Rücksprungadressen nicht aus – warum muss man noch zusätzlich die Parameter sowie lokale Variablen einer Funktion sichern? Warum musste man eine ganze Datenstruktur (Stack) dafür erfinden und warum hat ein Prozessor sogar spezielle Stapelregister, die anscheinend nur dazu da sind, um die Daten im Stack zu verwalten/verarbeiten? Würde das nicht mit temporären Variablen ohne Stack funktionieren, die notwendige Informationen für den Funktionsaufruf temporär speichern?
Zusätzlich wird auf der Notiz soviel Platz gelassen, dass alle lokalen Variablen untergebracht werden können, so dass jede Funktion eigene, lokale Variablen definieren kann.
Wenn bereits alle lokalen Variablen und die Parameter gesichert wurden, warum soll man danach noch irgendwelche Variablen definieren? Oder ist damit gemeint, dass bevor die lokalen Variablen gesichert werden können, zuerst für diese Variablen der benötigte Speicherplatz berechnet und reserviert wird, und erst danach werden diese lokalen Variablen gesichert?
Danach wird von einer Notiz abgelesen woher die Funktion aufgerufen wurde, damit der Prozessor die nächsten Anweisungen findet und die Notiz mit dem Ergebnis hinterlegt. Die rufende Funktion kann sich nun das Ergebnis holen. Eine solche Notiz, mit der eine Funktion arbeitet und ihre lokalen Variablen ablegt, nennt man Frame (zu Deutsch Rahmen). Das ist der Rahmen in dem sich eine Funktion mit seinen lokalen Variablen bewegen darf und den sie nicht verlassen darf.
Ist mit der Notiz die Speicherstelle im Stack gemeint, die zum Zwischenspeichern der Ergebnisse verwendet wird? Und mit dem Rahmen ist der Speicherbereich im Stack gemeint, der für eine Funktion reserviert wurde? Und die Funktion kennt die Anfangs- und Endadresse von diesem Speicherbereich – wie ein Array?
Der Compiler hat für die Funktion main zusammengerechnet, dass ihr Frame aus einer lokalen Variable (int temp, 4 Byte) und einem Rückgabewert (int, 4 Byte) besteht.
Ist mit dem Rückgabewert return 0 gemeint oder der Rückgabewert der Funktion add() oder printf()? Und was ist mit der Funktion add(4, 5)? Ich übergebe zwei Konstanten vom Typ int 4 und 5. Sollte man dafür nicht Platz in der main()-Funktion reservieren, wie wir das für die Variable int temp gemacht haben? Wir übergeben ja nur die Kopien dieser zwei Konstanten an die Funktion add(), also existieren diese zwei Konstanten vom Typ int in der Funktion main()? Und in der Funktion printf() übergeben wir eine ganze Zeichenkette und es wird dafür auch kein Platz in der main()-Funktion reserviert.
Dazu kommt die Rücksprungadresse (4 Byte für 32Bit-Systeme oder 8 Byte bei 64Bit-Systemen).
Warum ist Integer bei den 64 bit Systemen größer? Warum hat man das nicht einheitlich gemacht und für 64 bit Systeme spezielle Datentypen eingeführt z.B. int64 ?
Das geht äußerst schnell, da man den Stackpointer, der auf den Speicherbereich des Frames der aktuellen Funktion zeigt, lediglich um 12 bzw. 16 Byte verschiebt. Diese Frames liegen im Speicher also direkt hintereinander.
Sind die Anfangs- und Endadresse für jede Funktion in dem Stack gespeichert und der Prozessor sieht anhand von diesen Adressen wie viel Platz eine Funktion im Stack einnimmt? Und um die Daten aus dem Stack zu bearbeiten benutzt er den Stapelregister? Und sind mit dem Stackpointer einfach die Adressen von Funktionen im Stack gemeint? Also genau so wie ein Array, das eine Anfangs- und eine Endadresse besitzt.
Das geht äußerst schnell, da man den Stackpointer, der auf den Speicherbereich des Frames der aktuellen Funktion zeigt, lediglich um 12 bzw. 16 Byte verschiebt.
Ich verstehe das mit dem „Verschieben“ leider nicht – bzw. kann mir das noch nicht im Kopf vorstellen.
Auch hier hat der Compiler die Framegröße berechnet: 2 Integer als Parameter, 1 Integer als Rückgabe und 1 Integer als lokale Variable: 16 Bytes + Rücksprungadresse ergibt eine Framegröße 20 Bytes (auf 64 Systemen 24 Bytes), die nun auf den Stack „gepusht“ werden (der Stackpointer wird also 20 bzw. 24 Bytes verschoben).
Wie kommt man auf die Zahl von 24 Byte auf 64 bit Systemen – wie wird das berechnet – sollte das nicht 5 Integers * 8 Byte = 40 Byte sein (1 int auf 64 bit = 8 byte)? Verstehe leider wieder nicht, was mit „verschoben“ gemeint ist.
Mit dem Rücksprung wird das Frame vom Stack genommen. Dies nennt man „pop“ - der Stackpointer wird die 20 Byte (bzw. 24 Byte) zurückgeschoben.
Warum wird das vom Stack genommen und warum wird zurückgeschoben?
Der Stack ist natürlich nicht nur für die Programmiersprache selbst vorgesehen, moderne Computer verwenden diese Datenstruktur nur, um die Aufrufreihenfolge der Funktionen zu organisieren. Es steht jedem frei, Stacks auch für eigene Programme zu implementieren.
Wie bzw. warum muss man Stack implementieren? Ich habe es so verstanden, dass der Stack essentiell ist und IMMER verwendet wird, wenn man eine Funktion aufruft – also so gut wie immer, da die meisten Programme nur aus Funktionen bestehen – ohne globalen Variablen. Werden nur globale Variablen ohne Stack verwaltet bzw. irgendwo willkürlich an beliebigen Stellen im Arbeitsspeicher gespeichert?
In der kommenden Lektion werden wir uns damit beschäftigen, wie man Strukturen organisiert und eine neue Datenstruktur kennen lernen: Listen.
Sind Listen also eine andere Datenstruktur? Gibt es dafür auch ein Register im Prozessor? In dem Beispiel oben aus dem Youtube-Video werden aber die Daten mittels von Listen auf den Stack geschrieben, deshalb dachte ich, dass Listen bzw. Strukturen genau so wie Funktionen auf dem Stack liegen? (total verwirrt

)
So, das sind Stellen, die ich in dem Artikel nicht verstehe.
Danke!