Einführung
Aufgrund der Verbesserung und Verbilligung der Technologie wächst heute die Menge an Speicher und Verarbeitungsleistung stetig.
Nach Moores Gesetz:
Die Anzahl der auf einem integrierten Schaltkreischip platzierten Transistoren verdoppelt sich alle 24 Monate.
Es ist zu beachten, dass zwei Parameter geändert werden:
- Anzahl der Transistoren
- Modulabmessungen
Die gleichen Prinzipien werden auf die RAM-Größe (DRAM) projiziert.
Jetzt ist das Problem des Gedächtnisses nicht akut, da sich die Speichermenge in den letzten 10 Jahren pro Würfel um das 16-fache erhöht hat.
Die meisten High Level Programming Languages (PL) sind bereits "out of the box" und verbergen die Arbeit mit dem Speicher vor dem Programmierer. Und da diese Frage geschlafen hat, erscheint eine neue Kaste von Programmierern, die nicht verstehen
oder nicht verstehen
wollen, wie die Arbeit mit dem Gedächtnis funktioniert.
In diesem Thema werden die wichtigsten Punkte der Arbeit mit dem Speicher am Beispiel der C ++ - Sprache betrachtet, da es sich um eine der wenigen zwingenden Sprachen handelt, die die direkte Arbeit mit dem Speicher und OOP unterstützt.
Wofür ist IT?
Es ist erwähnenswert, dass dieser Artikel für Leute gedacht ist, die gerade erst mit C ++ beginnen oder nur eine Vorstellung vom dynamischen Speicher haben möchten.
Zur Laufzeit reserviert jedes Programm ein Stück Speicher für sich im DRAM. Alle anderen freien DRAM-Speicherplätze werden als
"Heap" (englisch "Heap") bezeichnet. Die Zuweisung von Speicher während der Ausführung für die Anforderungen des Programms erfolgt genau aus dem Heap und wird als Zuweisung von dynamischem Speicher bezeichnet.
Das ganze Problem ist, dass, wenn Sie sich nicht darum kümmern, den zugewiesenen Speicher zu bereinigen, wenn er nicht mehr benötigt wird, ein sogenannter Speicherverlust auftreten kann, bei dem Ihr System
(Programm) einfach hängt. Ähnlich wie ein Auto, das mitten auf der Straße stehen blieb, weil jemand vergessen hatte, es rechtzeitig aufzutanken.
Was du schon wissen solltestDie meisten modernen PLs sind mit Müllsammlern ausgestattet und löschen ihren Speicher selbstständig.
C ++ hat sich jedoch als eine der APIs mit der schnellsten Leistung etabliert, auch weil die gesamte Arbeit mit dem darin enthaltenen Speicher manuell ausgeführt wird.
neu und löschen
Die Speicherzuordnung kann statisch und dynamisch sein. Die statische Speicherzuweisung wird als einmalige Speicherzuweisung während der Programmkompilierung bezeichnet, und die Größe des statischen Speichers ändert sich zur Laufzeit nicht. Ein klassisches Beispiel ist die Deklaration einer ganzzahligen Variablen oder eines Arrays. Was aber, wenn der Programmierer nicht im Voraus weiß, wie viele Elemente im Container benötigt werden?
Die Verwendung von dynamischem Speicher ist ratsam, wenn die Speicherzuweisung für die Anforderungen des Programms nach Bedarf organisiert werden muss.
Der
neue Operator ist für die Zuweisung des dynamischen Speichers in C ++ verantwortlich, und
delete ist für die
Löschung verantwortlich.
Der
neue Operator gibt dem Ergebnis seiner Operation einen Zeiger auf eine neue Instanz der Klasse zurück.
Die Syntax lautet wie folgt:
|
Datentyp (T1) Zeiger | * |
Zeigername | =
neu |
Typ T1 |;
Nach dem
neuen Operator können Sie beispielsweise den Konstruktor verwenden, um die Felder der Klasse zu initialisieren.
Es ist anzumerken, dass der gleiche Speicherverlust genau dann auftritt, wenn der Programmierer die Kontrolle über seine Zuordnung verliert.
Es ist wichtig, sich zu erinnern:
Wenn Sie vergessen haben, den dynamischen Speicher der "verbrauchten" unnötigen Elemente zu löschen, wird früher oder später ein kritischer Moment eintreten, in dem der Speicher einfach nicht mehr benötigt wird.
Ein Beispiel für die Speicherzuweisung und deren Reinigung:
int main{
In diesem Artikel werden die sogenannten „intelligenten“ Zeiger nicht behandelt, da das Thema sehr umfangreich ist. Kurz gesagt: „Intelligente Zeiger automatisieren teilweise das Löschen des Speichers für den Programmierer.“
Zeiger
Zeiger sind für die Arbeit mit dynamischem Speicher in C ++ verantwortlich. Dies ist ein Thema, von dem Appetit Anfänger verwöhnt.
Sie können einen Zeiger mit dem Operator
* deklarieren. Standardmäßig zeigt es auf einen zufälligen Speicherbereich. Damit wir auf den benötigten Speicherbereich zugreifen können, müssen wir einen Link (Operator
& ) an die gewünschte Variable übergeben.
Der Zeiger selbst ist einfach die Adresse einer Speicherzelle, und um auf die in dieser Zelle gespeicherten Daten zuzugreifen, muss er dereferenziert werden.
Wichtiger Rückzug
Wenn Sie versuchen, den Zeiger ohne Dereferenzierung anzuzeigen, wird anstelle des Werts aus dem Speicherbereich, auf den er zeigt, die Adresse dieses Speicherbereichs angezeigt.
Um einen Zeiger zu dereferenzieren, setzen Sie einfach den Operator * vor seinen Namen.
int main() {

Anhand solcher Beispiele möchte ich fragen: „Warum ist das überhaupt notwendig, wenn Sie sofort eine Variable ableiten können?“
Ein weiteres Beispiel:
Wir haben eine Programmiererklasse, die Mitglieder eines Teams von Programmierern beschreibt,
die nichts über Zeiger wissen. class Programmers{ public: Programmers(){} Programmers(int iWeight, int iAge){ this->weight = iWeight; this->age = iAge; } int weight; int age; }; int main() {
Auf diese Weise kann das Gedächtnis nach Belieben manipuliert werden. Und deshalb können Sie sich bei der Arbeit mit dem Gedächtnis "in den Fuß schießen". Es ist zu beachten, dass die Arbeit mit dem Zeiger viel schneller ist, da der Wert selbst nicht kopiert wird, sondern ihm nur ein Link zu einer bestimmten Adresse zugewiesen wird.
Ein so beliebtes Schlüsselwort liefert übrigens einen Zeiger auf das aktuelle Klassenobjekt.
Diese Hinweise sind überall.Ein Beispiel für einen Zeiger im Alltag:Stellen Sie sich eine Situation vor, in der Sie ein Gericht in einem Restaurant bestellen. Um eine Bestellung aufzugeben, müssen Sie nur auf das Gericht im Menü zeigen und schon werden Sie vorbereitet. Auf die gleiche Weise geben andere Besucher des Restaurants den gewünschten Menüpunkt an. Somit ist jede Zeile im Menü ein Zeiger auf die Kochfunktion eines Gerichts, und dieser Zeiger wurde in der Entwurfsphase dieses Menüs selbst erstellt.
Zurück zu unseren Programmierern. Angenommen, wir müssen jetzt die Klassenfelder in den
privaten Bereich verschieben, wie es dem Prinzip der Kapselung von OOP entspricht. Dann müssen wir
Getter verwenden , um Lesezugriff auf diese Felder zu erhalten. Aber stellen Sie sich vor, wir haben nicht 2 Felder, sondern 100, und dafür müssen wir für jedes einen eigenen Accessor schreiben?
SpoilerNatürlich nicht, ich verstehe nicht einmal, warum Sie diesen Spoiler geöffnet haben.
Zu diesem Zweck machen wir einen "Accessor" vom Typ void und übergeben ihm Argumente als Referenz. Die Übergabe eines Arguments als Referenz bedeutet, dass der Wert des Arguments nicht kopiert wird, sondern nur die Adresse des realen Arguments übertragen wird. Wenn Sie also den Wert eines solchen Arguments ändern, ändern sich auch die Daten in der Speicherzelle des aktuellen Arguments.
Dies wirkt sich auch auf die Gesamtleistung aus, da das Übergeben eines Arguments als Referenz schneller ist als das Übergeben eines Werts. Ganz zu schweigen von den großen Elementensammlungen.
Beispielsweise
ändert die
darin enthaltene Methode
getParams die eingehenden Argumente und ihre Werte, auch im Bereich, von wo aus sie aufgerufen wurden.
Ein Zeiger hilft uns beim Navigieren im Array. Aus der Theorie der Datenstrukturen wissen wir, dass ein Array ein kontinuierlicher Speicherbereich ist, dessen Elemente nacheinander angeordnet sind.
Dies bedeutet, dass Sie jedes Element erreichen können, wenn Sie den Wert des Zeigers auf die Anzahl der Bytes ändern, die das Element im Array belegt, bis der Zeiger die Grenzen des Arrays überschreitet.
Erstellen Sie einen weiteren Zeiger, der auf das erste Element des
Programmierer- Arrays zeigt.
class Programmers{ public: Programmers(){} Programmers(int iWeight, int iAge){ this->weight = iWeight; this->age = iAge; }

In diesem Beispiel möchte ich Ihnen die Essenz der Tatsache vermitteln, dass Sie auf einen anderen Speicherbereich zugreifen können, wenn Sie den Wert der Adresse des Zeigers ändern.
Datenstrukturen wie Listen, Vektoren usw. basierend auf Zeigern und daher als dynamische Datenstrukturen bezeichnet. Und um über sie zu iterieren, ist es korrekter, Iteratoren zu verwenden. Ein Iterator ist ein Zeiger auf ein Element der Datenstruktur und bietet Zugriff auf das Element des Containers.
Abschließend
Nachdem Sie das Thema Zeiger verstanden haben, wird die Arbeit mit dem Speicher zu einem angenehmen Teil der Programmierung, und insgesamt erscheint ein detailliertes Verständnis der Funktionsweise der Maschine mit dem Speicher und ihrer Verwaltung. In gewisser Weise steckt eine Philosophie hinter dem Konzept des „Arbeitens mit dem Gedächtnis“. An Ihren Fingerspitzen ändern Sie die Ladung auf den Platten selbst sehr kleiner Kondensatoren.