Show
Der Programmspeicher beinhaltet, wie der Name schon verrät, das Programm. In zahlreichen Sprachen ist er strikt vom Datenspeicher getrennt. Manche Sprachen erlauben aus programmiertechnischen Gründen keine globalen Variablen. Bei C++ sind diese zwar erlaubt, es wird aber zwischen Programm- und Datenspeicher grundsätzlich unterschieden. Neben dem Speicher für globale Variable bleiben noch zwei Bereiche für die Daten. Einer dieser Bereiche wird als Stapelspeicher oder kurz Stapel (stack) bezeichnet und wir haben ihn schon häufig in Anspruch genommen. Den zweiten Speicherbereich bezeichnet man als Haldenspeicher oder kurz als Halde (heap). Er dient der dynamischen Speicherverwaltung und wird in diesem Abschnitt umfassend behandelt. Für den Stapelspeicher gilt immer: Was zuletzt angefordert wurde, muss auch als erstes wieder freigegeben werden (LIFO: Last In – First Out). Wenn Sie innerhalb eines Blocks {;;;} also Variablen anlegen, werden diese auf dem Stack angelegt. Am Ende des Blocks verliert die Variable ihre Gültigkeit und der Speicher wird wieder freigegeben. Wenn Sie nun eine Funktion aufrufen, wird die aktuelle Programmadresse (also die Stelle im Programm, an der die Funktion aufgerufen wird, die sog. „Rücksprungadresse“) auf dem Stapel abgelegt. Innerhalb der Funktionen werden möglicherweise Variablen angelegt, die wiederum auf dem Stapel landen. Dass dies so geschehen soll, wird vom Compiler zur Übersetzungszeit festgelegt und ist somit eine statische Speicherverwaltung. Am Ende der Funktion werden die Speicherbereiche der Variablen wieder freigegeben und das Programm springt zur Rücksprungadresse, die jetzt wieder oben auf dem Stapel liegt. Somit befindet es sich jetzt wieder an der Stelle, an der die Funktion aufgerufen wurde. Speicher aus der Halde wird nicht geordnet vergeben. Sie können ihn zu einem beliebigen Zeitpunkt anfordern und müssen ihn auch selbst wieder freigeben. Somit kann innerhalb einer Funktion Haldenspeicher angefordert, und nach Beendigung der Funktion ein Objekt, das auf der Halde liegt, weiterhin genutzt werden. Es wird also nicht mit Beendigung der Funktion ungültig. Versucht ein Objekt so, Speicher für sich zu reservieren, wird dieser im Rahmen der sogenannten dynamischen Speicherverwaltung zur Laufzeit festgelegt. In den folgenden Kapiteln lernen Sie in erster Linie, wie man in C++ mit Haldenspeicher arbeitet. Effektive Objekte können nur für den aktuellen Gültigkeitsbereich auf dem sog. Stack erstellt werden. Der Stapelspeicher ist ein Speicherbereich für lokale Variablen eines Moduls (statische Speicherverwaltung). Beim Verlassen eines Gültigkeitsbereichs werden diese Objekte automatisch zerstört. Alle vorigen Beispiele in diesem Abschnitt zeigen, wie Objekte auf dem Stack erstellt und zerstört werden. Größere Objekte wie große Speicherblöcke sollten nur in Beispielanwendungen auf dem Stack erstellt werden, denn dieser Bereich ist stark begrenzt und ist ausschließlich für lokale und temporäre Daten gedacht. Moderne Übersetzer begrenzen diesen Bereich auf 1 Megabyte. Wenn Sie größere Objekte auf den Stack legen wollen, müssen Sie die maximale Stackgröße modifizieren! Tun Sie dies nicht, erhalten Sie höchst bemerkenswerte Meldungen von Laufzeitumgebung, Betriebssystem oder Programmabbrüchen. Auf dem Heap (deutsch: Halde)BearbeitenEffektive Objekte können dynamisch und permanent bis zum Ende der Laufzeit des Moduls erstellt werden. Dies erfolgt im sog. Heap. Der Heap entspricht meistens dem nicht vorgespeicherten Datensegment für das gesamte Programm (dynamische Speicherverwaltung). Dazu verwendet man den Operator new. Wenn ein Objekt nicht mehr benötigt wird, muss es bei dieser Variante manuell zerstört werden und zwar mit dem Operator delete. Weiterführende Konzepte wie Smart-Pointer können das Zerstören beim Verlassen von Gültigkeitsbereichen automatisieren. Diese Operatoren kann man auch für Felder verwenden. Dann muss allerdings bei der Zerstörung der Operator delete [] verwendet werden. Der Heap hat den eklatanten Vorteil, dass die Grenzen des zuteilbaren Speichers nur vom Betriebssystem und der physikalischen Speichermenge gezogen werden und nicht von Compiler- und Linkereinstellungen. Ein weiterer Vorteil ist, dass alle Elemente einer Klasse dann auch auf dem Heap liegen. Wir verwenden Klasse 'a' aus vorigem Beispiel:
int main(){ A *pObjekt(0); // Zeiger auf ein A-Objekt pObjekt = new A; // Instanziieren auf dem Heap, Standardkonstruktor verwenden delete pObjekt; // Zerstören char *pszMemory = new char[0x100000]; // 1 Megabyte auf dem Heap allozieren delete [] pszMemory; // Speicherblock wieder freigeben A *ar_Objekte = new A[50]; // 50 Objekte von A anlegen delete [] ar_Objekte; return 0; } Weitere Optionen zur Verwendung von new, delete, new [] und delete [] gibt es auch.
Ein Stack ist eine Datenstruktur, die verwendet wird, um Daten in einer bestimmten Reihenfolge zu speichern. Zwei Operationen, die auf einem Stack ausgeführt werden können, umfassen eine Push-Operation, die ein Element in den Stack einfügt, und eine Pop-Operation, die das letzte Element entfernt, das in den Stack hinzugefügt wurde. Es folgt der Reihenfolge Last In First Out (LIFO). Jedes Mal, wenn ein Element hinzugefügt wird, wird es oben auf den Stapel gelegt und das einzige Element, das entfernt werden kann, ist das Element ganz oben im Stapel, genau wie ein Stapel von Objekten. Der Stapel befindet sich im Überlaufzustand, wenn er vollständig voll ist, und im Unterlaufzustand, wenn er vollständig leer ist. Stack kann einfach mit einem Array oder einer Linked List implementiert werden. Arrays sind schnell, aber in der Größe begrenzt, und die verknüpfte Liste erfordert einen Overhead zum Zuordnen, Verknüpfen, Aufheben und Aufheben der Zuordnung, ist jedoch in der Größe nicht beschränkt.
Anwendung des Stapels
Heap ist ein Spezialfall einer ausgeglichenen binären Baumdatenstruktur, bei der der Wurzelknotenschlüssel mit seinen Kindern verglichen und entsprechend angeordnet wird. Ein Heap respektiert nämlich die Heap-Eigenschaft: Jeder Knoten muss niedriger sein als jeder seiner Kinder oder sein unterstes Element an seiner Wurzel, um leicht zugänglich zu sein . Um einen binären Baum wie einen Heap darzustellen, besteht eine Implementierung darin, für jeden Knoten eine dynamische Zuordnung vorzunehmen, wobei 2 Zeiger auf seine Kinder zeigen. Ein Haufen kann auch durch die Darstellung in der Form einer realisiert werden Arrays , durch eine tun Ebene um Traversal des Haufens , wobei die Anordnung mit dem Element an der Wurzel beginnt, folgt dann mit den Kindern von dieser Wurzel, dann alle Kinder diese Kinder und dann die Urenkel und so weiter. Vorteile der Verwendung von Heap
Nachteile der Verwendung von Heap
Der Unterschied
Lesen Sie weiter: Verfahren vs. Objekt orientierte Programmierung Stapel vs. Haufen in Tabellenform
|