In Büchern über Programmiersprachen wird erklärt, dass Werttypen auf dem Stapel und Referenztypen auf dem Haufen erstellt werden, ohne zu erklären, was diese beiden Dinge sind. Ich habe keine klare Erklärung dazu gelesen. Ich verstehe, was ein Stapel ist. Aber,
Stapel:
Heap:
delete
, delete[]
, oder free
freigegeben.new
bzw. malloc
zugewiesen.Beispiel:
int foo()
{
char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
bool b = true; // Allocated on the stack.
if(b)
{
//Create 500 bytes on the stack
char buffer[500];
//Create 500 bytes on the heap
pBuffer = new char[500];
}//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
Der Stack Wenn Sie eine Funktion aufrufen, werden die Argumente für diese Funktion und einige andere Daten auf dem Stack abgelegt. Einige Informationen (z. B. wohin bei der Rückkehr) werden ebenfalls dort gespeichert. Wenn Sie eine Variable innerhalb Ihrer Funktion deklarieren, wird diese Variable ebenfalls auf dem Stack abgelegt.
Die Deallokation des Stacks ist recht einfach, da sie immer in der umgekehrten Reihenfolge wie die Allokation erfolgt. Beim Eintritt in eine Funktion wird Stapelmaterial hinzugefügt, beim Verlassen der Funktion werden die entsprechenden Daten entfernt. Das bedeutet, dass man in der Regel innerhalb eines kleinen Bereichs des Stacks bleibt, es sei denn, man ruft viele Funktionen auf, die wiederum viele andere Funktionen aufrufen (oder eine rekursive Lösung erstellen).
Der Heap Der Heap ist ein allgemeiner Name für den Ort, an dem Sie die Daten ablegen, die Sie im laufenden Betrieb erstellen. Wenn Sie nicht wissen, wie viele Raumschiffe Ihr Programm erstellen wird, werden Sie wahrscheinlich den Operator new (oder malloc oder einen vergleichbaren Operator) verwenden, um jedes Raumschiff zu erstellen. Diese Zuweisung wird eine Zeit lang bestehen bleiben, so dass es wahrscheinlich ist, dass wir die Dinge in einer anderen Reihenfolge freigeben, als wir sie erstellt haben.
Dadurch wird der Heap wesentlich komplexer, da sich ungenutzte Speicherbereiche mit freien Speicherbereichen abwechseln - der Speicher wird fragmentiert. Die Suche nach freiem Speicher in der benötigten Größe ist ein schwieriges Problem. Aus diesem Grund sollte der Heap vermieden werden (auch wenn er noch oft verwendet wird).
Implementierung Die Implementierung sowohl des Stacks als auch des Heaps obliegt in der Regel der Laufzeitumgebung/dem Betriebssystem. Spiele und andere Anwendungen, bei denen es auf die Leistung ankommt, entwickeln oft ihre eigenen Speicherlösungen, die einen großen Teil des Speichers aus dem Heap nehmen und ihn dann intern verteilen, um sich nicht auf das Betriebssystem verlassen zu müssen.
Dies ist nur dann sinnvoll, wenn die Speichernutzung von der Norm abweicht - z. B. bei Spielen, bei denen man einen Level in einem einzigen großen Vorgang lädt und den gesamten Speicher in einem weiteren großen Vorgang wieder wegschmeißen kann.
Physikalischer Ort im Speicher Dies ist weniger relevant, als Sie denken, denn es gibt eine Technologie namens Virtueller Speicher, die Ihr Programm glauben lässt, dass Sie Zugriff auf eine bestimmte Adresse haben, während sich die physischen Daten irgendwo anders befinden (sogar auf der Festplatte!). Die Adressen, die Sie für den Stack erhalten, sind in aufsteigender Reihenfolge, je tiefer Ihr Aufrufbaum wird. Die Adressen für den Heap sind nicht vorhersehbar (d.h. implimentationsspezifisch) und offen gesagt nicht wichtig.
Der Stack ist ein Teil des Speichers, der mit mehreren wichtigen Assembler-Befehlen manipuliert werden kann, z. B. "pop" (einen Wert vom Stack entfernen und zurückgeben) und "push" (einen Wert auf den Stack schieben), aber auch "call" (ein Unterprogramm aufrufen - damit wird die Adresse für die Rückkehr auf den Stack geschoben) und "return" (Rückkehr aus einem Unterprogramm - damit wird die Adresse vom Stack entfernt und dorthin gesprungen). Der Stack ist der Speicherbereich unterhalb des Stack-Pointer-Registers, der je nach Bedarf eingestellt werden kann. Der Stack wird auch für die Übergabe von Argumenten an Unterprogramme und für die Aufbewahrung der Werte in Registern vor dem Aufruf von Unterprogrammen verwendet.
Der Heap ist ein Teil des Speichers, der einer Anwendung vom Betriebssystem zur Verfügung gestellt wird, normalerweise durch einen Syscall wie malloc. Bei modernen Betriebssystemen ist dieser Speicher ein Satz von Seiten, auf die nur der aufrufende Prozess Zugriff hat.
Die Größe des Stacks wird zur Laufzeit bestimmt und wächst im Allgemeinen nicht mehr an, nachdem das Programm gestartet wurde. In einem C-Programm muss der Stack groß genug sein, um jede in jeder Funktion deklarierte Variable aufzunehmen. Der Heap wächst bei Bedarf dynamisch an, aber der Aufruf erfolgt letztlich durch das Betriebssystem (es vergrößert den Heap oft um mehr als den von malloc angeforderten Wert, so dass zumindest einige künftige mallocs nicht zum Kernel zurückkehren müssen, um mehr Speicher zu erhalten. Dieses Verhalten ist oft anpassbar)
Da Sie den Stack vor dem Start des Programms allokiert haben, müssen Sie niemals malloc ausführen, bevor Sie den Stack verwenden können, was einen kleinen Vorteil darstellt. In der Praxis ist es sehr schwer vorherzusagen, was in modernen Betriebssystemen mit virtuellem Speicher-Subsystem schnell und was langsam sein wird, denn wie die Seiten implementiert sind und wo sie gespeichert werden, ist ein Detail der Implementierung.