Hallo und willkommen zurück. In dieser Vorlesung möchte ich über die dynamische Speicherzuweisung sprechen.
Dies steht in direktem Zusammenhang mit Zeigern. Sie können dies nur
für Zeiger verwenden. Also lasst uns anfangen.
Wenn Sie also eine Variable in
c definieren, weist der Compiler Ihnen basierend auf dem Datentyp automatisch die richtige Speichermenge zu.
Wir haben das überall gesehen. Wenn wir Ganzzahlen, Floats oder Doubles erstellen, müssen wir keinen Speicher zuweisen.
Der Compiler erledigt das für Sie. Es ordnet tatsächlich etwas Speicher zu.
Gleiches gilt übrigens für Zeiger. Wenn Sie einen Zeiger erstellen,
wird Ihnen Speicher zugewiesen, sodass ein Zeigertyp, eine Adresse, gespeichert wird.
Das bedeutet nicht, dass Speicher für diese Adresse zugewiesen ist.
Bis zu diesem Zeitpunkt haben wir immer eine vorhandene Variable
zugewiesen, wenn wir einem Zeiger Daten zuweisen.
Wenn wir also einem Zeiger
Daten zuweisen möchten, haben wir die kaufmännische Und-Adresse des Operators
für eine Variable verwendet, der bereits Speicher zugewiesen wurde.
Was aber, wenn Sie einen Zeiger erstellen und ihm nicht die Adresse des Operators
zuweisen möchten, möchten Sie dynamisch Speicher zuweisen. Darum geht es in dieser ganzen Vorlesung.
Daher ist es häufig wünschenswert, Speicher dynamisch zuweisen zu können, während
ein Programm ausgeführt wird. Ordnen Sie den Speicher nach Bedarf zu.
Wenn Sie über ein Programm verfügen, mit dem ein Datensatz aus einer Datei in
ein Array im Speicher eingelesen werden kann, haben Sie drei Möglichkeiten.
Sie können das Array so definieren, dass es die maximale Anzahl möglicher Elemente enthält, die kompiliert werden.
Es hat eine feste Größe.
Wenn Sie das Array mit 50 Elementen definieren,
enthält es nur 50 Elemente. Der Compiler stellt fest, dass es 50 ist.
Er wird so viel Speicher zuweisen, wenn er ausgeführt wird.
Sie können ein Array mit variabler Länge verwenden, um die Größe der Array-Laufzeit zu dimensionieren.
Wir haben ein wenig über diese Arrays mit variabler
Länge gesprochen, sie werden erst in C11 unterstützt. Sie nehmen tatsächlich C11 heraus, sie fügen C99 hinzu.
Hier können Sie Variablen eingeben.
Dies ist jedoch immer noch eine feste Größe,
da alles, was variabel verknüpfte Arrays bedeutet, nur bedeutet, dass Sie Variablen für Größen übergeben können.
Aber sie sind immer noch statisch.
Oder die beste Wahl ist, dass Sie das Array nach Bedarf dynamisch zuordnen können.
Und es gibt verschiedene c-Funktionen, die Sie dazu aufrufen können.
Es wird also sehr nützlich sein, Speicher dynamisch zuweisen zu können.
Bei dem ersten Ansatz, den wir bisher durchgeführt haben, ist es eine feste Größe,
ob es sich um ein Array oder um einen Datentyp handelt.
Sie müssen Ihr Array so definieren, dass es die maximale Anzahl von Elementen enthält,
die direkt in einem Array enthalten sind.
Sie würden also so etwas tun, int dataArray [1000].
Dies bedeutet jedoch, dass die Datendatei nicht mehr als tausend Elemente enthalten kann.
Wenn dies der Fall ist, wird Ihr Programm unterbrochen.
Sie müssen also nur hoffen, dass die Datei, in der Sie gerade lesen, nicht viele Daten enthält.
Ihr Programm funktioniert nicht mit großen Dateigruppen oder großen Dateien.
Und wenn Sie mit großen Dateien arbeiten möchten, müssen Sie zurückgehen und das Array vergrößern.
Sie müssen die Größe ändern, aber
dann müssen Sie sie für all diese Dinge neu kompilieren. Es ist Zeit für die Kompilierung.
Unabhängig davon, welchen Wert Sie auswählen, haben Sie immer die Möglichkeit, in
Zukunft erneut auf dasselbe Problem zu stoßen, da es sich um eine feste Größe handelt.
Diese Größe wird beim Kompilieren des Codes festgelegt
und beim Ausführen des Programms nicht angepasst.
Es ändert sich nicht dynamisch.
Mithilfe dynamischer Speicherzuweisungsfunktionen können Sie Speicher nach Bedarf abrufen.
Mit diesem Ansatz können Sie Speicher zuweisen, während das Programm ausgeführt wird.
Denken Sie also an die Vorteile. Zunächst erstellen Sie nicht
viel Speicher, den Sie möglicherweise nicht verwenden.
Sie werden nur so viel Speicher erstellen, wie Sie benötigen.
Und wenn Sie mehr benötigen, können Sie mehr zuweisen.
Sie müssen nicht zurückgehen und neu kompilieren und die Größe ändern.
Eine dynamische Speicherzuweisung ist also der richtige Weg.
Dies hängt vom Konzept eines Zeigers ab
und bietet einen starken Anreiz, Zeiger in Ihrem Code zu verwenden.
Wir haben also über die Vorteile von Zeigern
gesprochen, und Sie haben sich vielleicht gut gedacht, okay, sie sind ein cooles Konzept,
aber warum müssen wir sie
verwenden? Dies ist einer der großen Gründe, damit Sie Speicher dynamisch zuweisen können.
Und was ist das? Bei C geht es darum, eine effiziente Sprache zu sein
und nicht viel Speicher zu verwenden.
C läuft also auf eingebetteten Systemen, Echtzeitprogrammierung und dies
ist einer der Gründe dafür, weil Sie den Speicherbedarf je
nach Bedarf begrenzen können.
Durch die dynamische Speicherzuweisung kann der Speicher zum Speichern von Daten dynamisch zugewiesen werden, wenn
das Programm ausgeführt wird.
Das dynamische Zuweisen von Speicher ist nur möglich,
weil Zeiger verfügbar sind.
Da Sie eine Adresse haben und ändern
können, auf wie viel der Zeiger zeigt, können Sie den Speicher neu einstellen.
Die meisten Produktionsprogramme verwenden eine dynamische Speicherzuordnung.
Wenn Sie Code in c schreiben, werden Sie diesen verwenden.
Selbst wenn Sie ein Anfänger sind, werden Sie
die dynamische Speicherzuweisung verwenden. Die meisten nützlichen Programme verwenden dieses Konzept.
Durch die dynamische Zuweisung von Daten können Sie zur Laufzeit Zeiger erstellen,
die gerade groß genug sind, um die für die Aufgabe erforderliche Datenmenge
aufzunehmen, sodass Sie keinen Speicher verschwenden. Vital.
Wir müssen auch über den Haufen gegenüber dem Stapel sprechen.
Es gibt zwei Möglichkeiten, wie Sie Speicher in einem Programm speichern können, zwei
verschiedene Datenstrukturen, in denen Sie Daten auf dem Stapel speichern können, oder Sie können Daten auf
dem Heap speichern.
Die dynamische Speicherzuweisung reserviert alles auf dem Heap.
Wir werden nicht im Detail darauf eingehen, wie diese organisiert sind, sondern im Wesentlichen.
Wenn Sie Daten auf dem Heap speichern, können weitere
Änderungen vorgenommen werden.
Sie können die Größe von Datenobjekten beliebig ändern und es bleibt viel
länger bestehen, wenn Sie Speicher in einem Heap zuweisen. Es
bleibt bei der Gesamtheit Ihres Programms.
Wenn Sie einen Stapel verwenden, ist dies einschränkender.
Der Stack wird für alles verwendet, von Funktionsargumenten bis hin zu lokalen Variablen.
Dort wird alles gespeichert.
Und deshalb haben wir über den Call-Stack gesprochen.
Es wurden Methoden und
Funktionen untersucht. Ich benutze ab und zu Methoden, aber Methoden sind wirklich ein
objektorientierter Begriff, also Funktionen.
Alles, was mit Funktionen zu tun hat, wird auf dem Stapel erstellt.
Das heißt also, wenn Sie eine Funktion ausführen,
wird sie auf den Stapel gelegt. Die lokalen Variablen werden auf den Stapel gelegt.
Wenn Sie die Funktion verlassen, wird alles gelöscht.
Der auf dem Stapel befindliche Speicher bleibt also nicht lange erhalten.
Dies ist einer der Gründe, warum Sie beim dynamischen Zuweisen
von Speicher auf
dem Heap steuern, wann Sie ihn verwenden und wann Sie ihn nicht verwenden, ihn zuweisen und löschen.
Sachen auf dem Stapel, die du nicht kontrollierst. Es wird
automatisch gelöscht, automatisch zugewiesen.
So werden lokale Variablen, Variablen innerhalb von Funktionen wie der Hauptvariablen, die
auf dem Stapel erstellt werden und so weiter.
Der Speicher auf dem Heap ist anders, er wird von Ihnen, dem Programmierer, gesteuert.
Wenn Sie Speicher auf dem Heap zuweisen, liegt es an Ihnen, zu verfolgen, wann der von Ihnen
zugewiesene Speicher nicht mehr benötigt wird, sodass Sie den Speicher löschen müssen.
Sie müssen den Speicherplatz freigeben,
wenn er nicht mehr von Ihnen verwendet wird. Und das ist wichtig,
weil das Gedächtnis eine sehr, sehr knappe Ressource ist.
Heute haben wir viel Speicher, auf dem Sie einen Computer betreiben können, 12 Gigs, 16
Gigs, was auch immer es ist. Aber für Programme, die
eingebettet sind und auf Geräten ausgeführt werden, haben Sie
nicht unbedingt so viel Speicher. Sie möchten also steuern, wie viel Speicher verwendet
wird, und Sie möchten den Speicher auch freigeben, wenn Sie ihn nicht mehr verwenden,
damit andere Anwendungen ihn verwenden können.
Wiederum wichtiger in eingebetteten Systemen auf niedriger Ebene.
Aber dies ist eine Freiheit, die viele Sprachen nicht haben. Diese Fähigkeit, den
Speicher Ihres Managers durch den Programmierer zu steuern,
kann das Programm viel effizienter und schneller machen.
In der nächsten Vorlesung werden wir darüber sprechen, wie Sie dies tatsächlich
tun können und welche Funktionen Sie aufrufen können. Offensichtlich gibt es verschiedene Funktionen, die Sie aufrufen
können, um Speicher in freien Speicher zuzuweisen. Vielen Dank.