Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lernen Verstehen von Zeigern und Heap-Speicher | Einführung in Smart Pointer
C++ Smart Pointers
course content

Kursinhalt

C++ Smart Pointers

C++ Smart Pointers

1. Einführung in Smart Pointer
2. Einzigartige Zeiger
3. Gemeinsame Zeiger
4. Schwache Zeiger
5. Referenzen
6. Fortgeschrittene Themen

book
Verstehen von Zeigern und Heap-Speicher

Was ist ein Zeiger

Zeiger sind ein wesentliches Konzept in der Welt von C++. Anstatt tatsächliche Daten zu speichern, speichern Zeiger Speicheradressen, was es Entwicklern ermöglicht, effizient auf verschiedene Teile des Speichers zuzugreifen und diese zu manipulieren.

Hinweis

Sie können sich Zeiger wie GPS-Koordinaten vorstellen. Sie zeigen uns auf bestimmte Orte im Speicher und ermöglichen uns den Zugriff auf die dort gespeicherten Daten.

Wenn Sie einen Zeiger deklarieren, erstellen Sie eine Variable, die die Speicheradresse einer anderen Variable enthält. Zum Beispiel können Sie einen Zeiger auf einen Integer so erstellen:

  • ptr hält die Speicheradresse des Integers myValue.
  • Das &-Zeichen wird verwendet, um die Speicheradresse der Variablen zu erhalten, mit der es verwendet wird.
  • &myValue ruft die Speicheradresse der Variablen myValue ab, die dann im Zeiger ptr gespeichert wird.

Nullzeiger

In C++ sind null oder nullptr spezielle Schlüsselwörter, die verwendet werden, um einen Nullzeiger darzustellen. Ein Nullzeiger zeigt auf keine gültige Speicheradresse. Es ist im Wesentlichen ein Zeiger ohne Ziel.

Hinweis

Wir können den Integer-Zeiger auch auf null (oder nullptr) initialisieren. Dies tun wir, wenn wir den Zeiger nicht sofort mit einem bestimmten Wert verknüpfen möchten.

Zeiger neu zuweisen

Zeiger können neu zugewiesen werden:

Zeigerarithmetik

Die Zeigerarithmetik ist ein faszinierender Aspekt von Zeigern. Sie ermöglicht es Ihnen, den Speicher zu durchlaufen, indem Sie die Adresse erhöhen oder verringern, die von einem Zeiger gehalten wird.

Betrachten Sie zum Beispiel den folgenden Code, in dem wir ein Array von Ganzzahlen erstellen und dann einen Zeiger definieren, um die Adresse des Arrays zu halten. Da ein Array mehrere Elemente enthält, speichert ein Zeiger standardmäßig die Adresse des ersten Elements, das in diesem Fall die Ganzzahl 1 ist.

Um auf das zweite Element zuzugreifen, können wir den Zeiger einfach um 1 erhöhen. Dann dereferenzieren wir ihn schließlich mit dem (*) Operator. Das Dereferenzieren gibt den Wert zurück, der an der Speicheradresse vorhanden ist, die vom Zeiger gehalten wird (in unserem Fall die Ganzzahl 2). Folgen Sie den Kommentaren im obigen Code, um zu verstehen, was passiert!

Dynamische Speicherzuweisung

Es gibt zwei Arten von Speicher, die in einem C++-Programm verwendet werden können: Stack und Heap. Der Stack-Speicher ist in seiner Größe begrenzt. Er eignet sich gut für kleine, kurzlebige Variablen, die über ihren Gültigkeitsbereich hinaus nicht benötigt werden. Wenn eine Variable im Stack ihren Gültigkeitsbereich verlässt, wird sie automatisch zerstört.

Wenn Sie jedoch Speicher benötigen, der länger als sein Gültigkeitsbereich bestehen bleibt oder mehr Platz benötigt, wenden Sie sich an den Heap. Der Heap ist ein viel größerer Speicherpool, in dem Ihr Programm zur Laufzeit Speicher anfordern kann. Dies wird typischerweise mit dem new-Schlüsselwort durchgeführt:

Hier haben wir Speicher für ein Integer auf dem Heap zugewiesen und seine Adresse in einem Zeiger gespeichert. Das Schwierige an Heap-Speicher ist, dass er nicht automatisch freigegeben wird. Es liegt in der Verantwortung des Entwicklers, ihn zu deallozieren, wenn er fertig ist. Für jedes new muss es ein entsprechendes delete geben.

Speicherlecks und hängende Zeiger

Wenn Sie Zeiger in Ihren C++-Code einführen, betreten Sie den Bereich der manuellen Speicherverwaltung. Manuelle Speicherverwaltung erfordert, dass Sie den Speicher freigeben, wenn er nicht mehr benötigt wird. Dies kann zu häufigen Fallstricken führen: Speicherlecks und hängende Zeiger.

Speicherlecks

Speicherlecks treten typischerweise auf, wenn Sie vergessen, den Speicher freizugeben. Solcher Speicher bleibt reserviert, was dazu führt, dass Ihr Programm im Laufe der Zeit Speicher verliert.

Beispiel

Sie erstellen dynamische Objekte in einer Schleife, löschen sie jedoch nie, was zu einem Speicherleck führt.

Hängende Zeiger

Hängende Zeiger treten auf, wenn ein Zeiger weiterhin auf einen Speicher zeigt, der freigegeben wurde. Es ist, als hätte man eine Schatzkarte, die einen zu einem Ort (Speicheradresse) führt, an dem der Schatz (Daten) bereits genommen (zerstört) wurde.

cpp

main

copy
12345678910111213
#include <iostream> int main() { int* ptr = new int; // dynamically allocate an integer *ptr = 42; // assign a value to the allocated memory std::cout << *ptr << std::endl; // This will output 42 delete ptr; // deallocate the memory std::cout << *ptr << std::endl; // ptr is a dangling pointer! }

Im obigen Code versuchen wir, auf einen Zeiger zuzugreifen, nachdem delete darauf aufgerufen wurde, was den Speicher freigibt, auf den er zeigte. Nach der Löschung wird der Zeiger zu einem hängenden Zeiger, da er weiterhin die Adresse eines Speichers hält, der nicht mehr existiert.

Der Zugriff auf einen hängenden Zeiger kann zu unvorhersehbarem Verhalten oder sogar Abstürzen führen, da der Speicher, auf den er zeigt, nicht mehr gültig ist. Um hängende Zeiger zu vermeiden, ist es wichtig, einen Zeiger auf "null" zu setzen, nachdem "delete" darauf aufgerufen wurde. Bevor man dann auf den Wert dieses Zeigers zugreift, ist es zwingend erforderlich zu überprüfen, ob er null ist oder nicht. Betrachten Sie den folgenden Code:

cpp

main

copy
1234567891011121314151617181920212223
#include <iostream> int main() { // Dynamically allocate an integer int* ptr = new int; // Assign a value to the allocated memory *ptr = 42; // Output the value, which will be 42 std::cout << *ptr << std::endl; // Free the allocated memory to avoid memory leaks delete ptr; // Set the pointer to null ptr = nullptr; // Check whether the pointer is not null if (ptr != nullptr) // Access and output the value, which won't execute std::cout << *ptr << std::endl; }
Hat der folgende Code ein Speicherleck?

Hat der folgende Code ein Speicherleck?

Wählen Sie die richtige Antwort aus

War alles klar?

Wie können wir es verbessern?

Danke für Ihr Feedback!

Abschnitt 1. Kapitel 1
We're sorry to hear that something went wrong. What happened?
some-alt