Werbung

Unabhängig davon, ob Sie es bemerken oder nicht, die überwiegende Mehrheit der von Ihnen verwendeten Programme verwendet Zeiger in irgendeiner Weise. Vielleicht haben Sie eine erlebt NullPointerException irgendwann. Als Programmierer verwendet der von Ihnen geschriebene Code höchstwahrscheinlich Zeiger, auch wenn Sie diese nicht selbst implementiert haben.

Heute zeige ich Ihnen, wie Zeiger funktionieren. Vielleicht möchten Sie es sich ansehen wie Arrays und Listen funktionieren Funktionsweise von Arrays und Listen in PythonArrays und Listen sind einige der nützlichsten Datenstrukturen in der Programmierung - obwohl nur wenige Menschen sie in vollem Umfang nutzen. Weiterlesen für einen Programmierprimer. Dieser Artikel wird theoretischer als gewöhnlich sein, aber bleiben Sie dabei, Zeiger sind sehr komplex!

Code kompilieren

Bevor Sie sich mit Zeigern befassen, müssen Sie verstehen, wie Code erstellt und ausgeführt wird - vielleicht wissen Sie das bereits. Dieser Abschnitt enthält ziemlich allgemeine Aussagen - Dinge, die für die gelten

Mehrheit von Sprachen, aber nicht unbedingt alle.

Zeiger

Lassen Sie uns die Dinge zurück zum Anfang bringen. Jeder Computer verwendet binär Was ist binär? [Technologie erklärt]Angesichts der Tatsache, dass Binärdateien für die Existenz von Computern so grundlegend sind, erscheint es seltsam, dass wir uns noch nie mit diesem Thema befasst haben. Daher hätte ich heute gedacht, ich würde einen kurzen Überblick darüber geben, welche Binärdateien ... Weiterlesen , eine Reihe von Einsen und Nullen, die die moderne Technologie, wie wir sie kennen, ausmachen. Es ist äußerst schwierig, irgendetwas in Binärform zu codieren (die Dateien wären sehr verwirrend), da dies die Rohanweisungen sind, die Sie benötigen Zentraleinheit oder CPU zu funktionieren Was ist eine CPU und was macht sie?Das Berechnen von Akronymen ist verwirrend. Was ist überhaupt eine CPU? Und brauche ich einen Quad- oder Dual-Core-Prozessor? Wie wäre es mit AMD oder Intel? Wir sind hier, um den Unterschied zu erklären! Weiterlesen . Dies ist bekannt als Maschinensprache.

Der nächste Schritt vom Maschinencode ist Versammlung. Dies ist ein etwas menschlich lesbares Format. Das Programmieren ist zwar noch komplex, aber möglich. Die Assembly besteht aus einer Reihe einfacher Befehle zum Ausführen von Aufgaben und wird als a bezeichnet niedriges Niveau Programmiersprache. Es ist möglich, komplexe Programme zu schreiben, aber es ist schwierig, abstrakte Konzepte auszudrücken, und es erfordert viel Überlegung.

Bei vielen Videospielen und Hochleistungsanwendungen ist ein Teil der Logik in Assembly geschrieben, da einige echte Geschwindigkeitssteigerungen festgestellt werden können, wenn Sie wissen, was Sie tun. Für die überwiegende Mehrheit der Programmierprojekte müssen Sie jedoch überhaupt keine Baugruppe kennen.

Zeiger

Also, wenn Maschinencode zu schwer zu schreiben und Assembly zu schwer zu programmieren ist, womit schreiben Sie Code? Hier ist wo hohes Level Sprachen kommen herein. Hochsprachen erleichtern das Schreiben von Programmen. Sie können in etwas programmieren, das Ihrer Muttersprache ähnelt, und es ist einfach, komplexe Algorithmen auszudrücken. Sie haben vielleicht schon von vielen Hochsprachen gehört (und Sie werden definitiv ein darin geschriebenes Programm verwendet haben):

  • BASIC
  • C ++
  • Lispeln

Diese Sprachen sind jetzt sehr alt und viele wurden in den frühen 1950er Jahren entwickelt! Fast jede moderne Programmiersprache ist eine Hochsprache, einschließlich PHP und Python. Es werden jeden Tag mehr Sprachen erfunden (obwohl es jetzt wahrscheinlich genug gibt), aber wie genau funktioniert Ihr Code noch richtig, wenn Computer Maschinencode benötigen?

Hier kommt die Zusammenstellung ins Spiel. Ein Compiler ist ein Programm, das Ihren Code auf hoher Ebene in ein Formular konvertiert, das ausgeführt werden kann. Dies könnte eine andere Hochsprache sein, aber es handelt sich normalerweise um Assembler. Einige Sprachen (wie Python oder Java) konvertieren Ihren Code in eine Zwischenstufe namens Bytecode. Dies muss zu einem späteren Zeitpunkt erneut kompiliert werden, was normalerweise bei Bedarf erfolgt, z. B. wenn das Programm ausgeführt wird. Dies ist bekannt als gerade rechtzeitig Zusammenstellung, und es ist sehr beliebt.

Speicherverwaltung

Nachdem Sie nun wissen, wie Programmiersprachen funktionieren, wollen wir uns die Speicherverwaltung in Hochsprachen ansehen. Für diese Beispiele verwende ich Pseudocode - Code, der nicht in einer bestimmten Sprache geschrieben ist, sondern eher zur Darstellung von Konzepten als zur genauen Syntax verwendet wird. Heute ähnelt dies hauptsächlich C ++, da dies (meiner Meinung nach) die beste Hochsprache ist.

In diesem Abschnitt ist es hilfreich, wenn Sie Verständnis dafür haben wie RAM funktioniert Eine schnelle und schmutzige Anleitung zum RAM: Was Sie wissen müssenRAM ist eine wichtige Komponente jedes Computers, kann jedoch verwirrend sein. Wir teilen es in leicht verständliche Begriffe auf, die Sie verstehen werden. Weiterlesen .

Die meisten Sprachen haben Variablen - Container, in denen einige Daten gespeichert sind. Sie müssen den Datentyp explizit definieren. Einige dynamisch typisierte Sprachen wie Python oder PHP erledigen dies für Sie, müssen es aber noch tun.

Angenommen, Sie haben eine Variable:

int myNumber;

Dieser Code deklariert eine Variable namens meine Nummerund gibt ihm einen Datentyp von ganze Zahl. Nach der Kompilierung interpretiert der Computer diesen Befehl wie folgt:

"Finden Sie einen leeren Speicher und reservieren Sie einen Speicherplatz, der groß genug ist, um eine Ganzzahl zu speichern."

Sobald dieser Befehl ausgeführt wurde, kann dieses Speicherbit nicht mehr von einem anderen Programm verwendet werden. Es enthält noch keine Daten, ist jedoch für Ihre Variable myNumber reserviert.

Weisen Sie Ihrer Variablen nun einen Wert zu:

myNumber = 10;

Um diese Aufgabe abzuschließen, greift Ihr Computer auf den reservierten Speicherort zu und ändert den dort gespeicherten Wert in diesen neuen Wert.

Nun, das ist alles schön und gut, aber wie werden Speicherorte nicht reserviert? Wenn Programme den gesamten gewünschten Speicher reservieren würden, würde sich der RAM sofort füllen - das würde zu einem sehr langsames System.

Zeiger

Um dieses potenzielle Problem zu vermeiden, implementieren viele Sprachen a Müllsammler, wird verwendet, um verschwundene Variablen zu zerstören (und damit die reservierten Speicherplätze freizugeben) außer Reichweite.

Sie fragen sich vielleicht, was der Umfang ist und warum er so wichtig ist. Der Bereich definiert die Grenzen und die Lebensdauer von Variablen oder von einem Programm verwendeten Speichern. Eine Variable ist "außerhalb des Gültigkeitsbereichs", wenn kein Code mehr auf sie zugreifen kann (dann greift der Garbage Collector ein). Hier ein Beispiel:

Funktion maths () {int firstNumber = 1; } int secondNumber = 2; print (firstNumber + secondNumber); // wird nicht funktionieren

Dieses Beispiel wird nicht kompiliert. Die Variable firstNumber ist innerhalb der Mathe Funktion, das ist also der Umfang. Es kann nicht von außerhalb der Funktion zugegriffen werden, in der es deklariert wurde. Dies ist ein wichtiges Programmierkonzeptund das Verständnis ist entscheidend für die Arbeit mit Zeigern.

Diese Art der Speicherbehandlung wird als bezeichnet Stapel. So funktioniert die überwiegende Mehrheit der Programme. Sie müssen keine Zeiger verstehen, um es zu verwenden, und es ist ziemlich gut strukturiert. Der Nachteil des Stapels ist die Geschwindigkeit. Da der Computer Speicher zuweisen, Variablen verfolgen und die Speicherbereinigung ausführen muss, entsteht ein geringer Overhead. Dies ist in Ordnung für kleinere Programme, aber was ist mit Hochleistungsaufgaben oder datenintensiven Anwendungen?

Geben Sie Folgendes ein: Zeiger.

Zeiger

An der Oberfläche klingen Zeiger einfach. Sie verweisen auf (zeigen auf) ein Speicherort. Dies scheint nicht anders zu sein als "normale" Variablen auf dem Stapel, aber glauben Sie mir, es gibt einen großen Unterschied. Zeiger werden auf dem gespeichert Haufen. Dies ist das Gegenteil des Stapels - er ist weniger organisiert, aber viel schneller.

Schauen wir uns an, wie Variablen auf dem Stapel zugewiesen werden:

int numberOne = 1; int numberTwo = numberOne;

Dies ist eine einfache Syntax. Die Variable Nummer zwei enthält die Nummer eins. Der Wert wird während der Zuweisung aus dem kopiert Nummer Eins Variable.

Wenn Sie das bekommen wollten Speicheradresse Für eine Variable müssen Sie anstelle ihres Werts das kaufmännische Und-Zeichen (&) verwenden. Dies nennt man das Adresse von Operator und ist ein wesentlicher Bestandteil Ihres Zeiger-Toolkits.

int numberOne = 1; int numberTwo = & numberOne;

Jetzt die Nummer zwei Variable Punkte an einen Speicherort, anstatt die Nummer eins in den eigenen neuen Speicherort zu kopieren. Wenn Sie diese Variable ausgeben würden, wäre sie nicht die Nummer eins (obwohl diese im Speicherort gespeichert ist). Es würde den Speicherort ausgeben (wahrscheinlich etwa 2167, obwohl er je nach System und verfügbarem RAM variiert). Um auf den in einem Zeiger gespeicherten Wert anstelle des Speicherorts zuzugreifen, müssen Sie Dereferenzierung der Zeiger. Dies greift direkt auf den Wert zu, der in diesem Fall die Nummer eins wäre. So dereferenzieren Sie einen Zeiger:

int numberTwo = * numberOne;

Das Dereferenzierungsoperator ist ein Sternchen (*).

Es kann schwierig sein, dieses Konzept zu verstehen. Lassen Sie uns es noch einmal durchgehen:

  • Das Adresse von Der Operator (&) speichert die Speicheradresse.
  • Das Dereferenzierungsoperator (*) greift auf den Wert zu.

Die Syntax ändert sich geringfügig, wenn Zeiger deklariert werden:

int * myPointer;

Der Datentyp von int hier bezieht sich auf den Datentyp der Zeiger Punkte zu und nicht den Typ des Zeigers selbst.

Jetzt, da Sie wissen, was Zeiger sind, können Sie einige wirklich nette Tricks damit machen! Wenn Speicher verwendet wird, wird Ihr Betriebssystem gestartet der Reihe nach. Sie können sich RAM als Taubenlöcher vorstellen. Viele Löcher, um etwas zu speichern, nur eines kann gleichzeitig verwendet werden. Der Unterschied besteht darin, dass diese Taubenlöcher alle nummeriert sind. Wenn Sie Speicher zuweisen, startet Ihr Betriebssystem mit der niedrigsten Nummer und arbeitet. Es wird niemals zwischen Zufallszahlen herumspringen.

Zeiger

Wenn Sie bei der Arbeit mit Zeigern ein Array zugewiesen haben, können Sie einfach zum nächsten Element navigieren, indem Sie den Zeiger einfach inkrementieren.

Hier wird es interessant. Wenn Sie Werte an eine Funktion übergeben (unter Verwendung von im Stapel gespeicherten Variablen), werden diese Werte in Ihre Funktion kopiert. Wenn dies große Variablen sind, speichert Ihr Programm sie jetzt zweimal. Wenn Ihre Funktion beendet ist, benötigen Sie möglicherweise eine Möglichkeit, diese Werte zurückzugeben. Funktionen können im Allgemeinen nur eine Sache zurückgeben - was ist, wenn Sie zwei, drei oder vier Dinge zurückgeben möchten?

Zeiger

Wenn Sie einen Zeiger auf Ihre Funktion übergeben, wird nur die Speicheradresse kopiert (die winzig ist). Das spart Ihrer CPU viel Arbeit! Vielleicht zeigt Ihr Zeiger auf ein riesiges Bildarray - Ihre Funktion kann nicht nur genau so funktionieren Daten, die genau am selben Speicherort gespeichert sind, müssen jedoch nicht mehr zurückgegeben werden etwas. Ordentlich!

Sie müssen jedoch sehr vorsichtig sein. Zeiger können immer noch außerhalb des Gültigkeitsbereichs liegen und vom Müllsammler gesammelt werden. Die im Speicher gespeicherten Werte werden jedoch nicht erfasst. Dies wird als Speicherverlust bezeichnet. Sie können nicht mehr auf die Daten zugreifen (da die Zeiger zerstört wurden), aber sie belegen immer noch Speicher. Dies ist ein häufiger Grund für den Absturz vieler Programme und kann bei großen Datenmengen spektakulär fehlschlagen. Meistens beendet Ihr Betriebssystem Ihr Programm, wenn Sie ein großes Leck haben (mit mehr RAM als das System), aber das ist nicht wünschenswert.

Zeiger

Das Debuggen von Zeigern kann ein Albtraum sein, insbesondere wenn Sie mit großen Datenmengen oder in Schleifen arbeiten. Ihre Nachteile und Schwierigkeiten beim Verstehen sind die Kompromisse wert, die Sie bei der Leistung erzielen. Denken Sie daran, dass sie möglicherweise nicht immer erforderlich sind.

Das war `s für heute. Ich hoffe, Sie haben etwas Nützliches über ein komplexes Thema gelernt. Natürlich haben wir nicht alles behandelt, was es zu wissen gibt - es ist ein sehr komplexes Thema. Wenn Sie mehr erfahren möchten, kann ich es nur empfehlen C ++ in 24 Stunden.

Wenn dies etwas komplex war, schauen Sie sich das an unser Leitfaden zu den einfachsten Programmiersprachen 6 Einfachste Programmiersprachen für AnfängerBeim Programmieren geht es ebenso darum, die richtige Sprache zu finden wie um den Erbauungsprozess. Hier sind die sechs einfachsten Programmiersprachen für Anfänger. Weiterlesen .

Haben Sie gelernt, wie Zeiger heute funktionieren? Haben Sie Tipps und Tricks, die Sie mit anderen Programmierern teilen möchten? Springe in die Kommentare und teile deine Gedanken unten mit!

Joe hat einen Abschluss in Informatik von der University of Lincoln, UK. Er ist ein professioneller Softwareentwickler, und wenn er keine Drohnen fliegt oder Musik schreibt, macht er oft Fotos oder produziert Videos.