Eine Einführung in Zeiger für Programmierer
Unabhängig davon, ob Sie es merken oder nicht, die meisten Programme, die Sie verwendet haben, verwenden in gewisser Weise Zeiger. Vielleicht hast du eine erlebt NullPointerException irgendwann. Als Programmierer verwendet Code, den Sie schreiben, höchstwahrscheinlich Zeiger, selbst wenn Sie sie nicht selbst implementiert haben.
Heute zeige ich Ihnen, wie Zeiger funktionieren. Vielleicht möchten Sie herausfinden, wie Arrays und Listen funktionieren. Wie Arrays und Listen in Python funktionieren. Wie Arrays und Listen in Python funktionieren. Arrays und Listen gehören zu den nützlichsten Datenstrukturen bei der Programmierung. - obwohl nur wenige Menschen sie voll ausschöpfen. Lesen Sie mehr für eine Programmiergrundierung. Dieser Artikel wird theoretischer sein als üblich, aber bleiben Sie dabei, Zeiger sind sehr komplex!
Code kompilieren
Bevor Sie nach Zeigern suchen, müssen Sie wissen, wie Code erstellt und ausgeführt wird - vielleicht wissen Sie das bereits. Dieser Abschnitt wird ziemlich allgemeine Aussagen enthalten - Dinge, die für das gelten Mehrheit von Sprachen, aber nicht unbedingt alle.
Lassen Sie uns die Dinge wieder an den Anfang bringen. Jeder Computer verwendet binäres Was ist binär. [Technologie erklärt] Was ist binär? [Technologie erklärt] Angesichts der Tatsache, dass Binärdaten für die Existenz von Computern so absolut grundlegend sind, erscheint es merkwürdig, dass wir das Thema noch nie zuvor in Angriff genommen haben. Heute dachte ich, ich würde einen kurzen Überblick darüber geben, welche binären… eine Reihe von Einsen und Nullen, aus denen sich moderne Technologie zusammensetzt, wie wir sie kennen. Es ist äußerst schwierig, etwas in binärem Code zu codieren (die Dateien wären sehr verwirrend), da dies die von Ihnen benötigten rohen Anweisungen sind zentrale Verarbeitungseinheit oder CPU zu funktionieren Was ist eine CPU und was macht sie? Was ist eine CPU und was macht sie? Berechnungsakronyme sind verwirrend. Was ist eigentlich 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 für Menschen lesbares Format. Während das Programmieren noch komplex ist, ist es möglich. Die Baugruppe besteht aus einer Reihe einfacher Befehle zur Ausführung von Aufgaben und wird als a bezeichnet niedriger Pegel Programmiersprache. Es ist möglich, komplexe Programme zu schreiben, aber es ist schwierig, abstrakte Konzepte auszudrücken, und erfordert viel Rücksicht.
Viele Videospiele und Hochleistungsanwendungen verfügen über eine gewisse Logik, die beim Zusammenbau geschrieben wird. Wenn Sie wissen, was Sie tun, können Sie Geschwindigkeitssteigerungen feststellen. Für die große Mehrheit der Programmierprojekte müssen Sie jedoch keine Baugruppe kennen.
Wenn also Maschinencode zu schwer zu schreiben ist und Assemblierung zu schwer zu programmieren ist, womit schreiben Sie Code? Hier ist wo hohes Level Sprachen kommen rein. Hochsprachen machen das Schreiben von Programmen einfach. Sie können in etwas programmieren, das Ihrer Muttersprache ähnelt, und komplexe Algorithmen lassen sich leicht ausdrücken. Möglicherweise haben Sie von vielen Hochsprachen gehört (und Sie haben definitiv ein Programm verwendet, das in ihnen geschrieben wurde):
- BASIC
- C++
- Lispeln
Diese Sprachen sind jetzt sehr alt und viele wurden in den frühen fünfziger Jahren entwickelt! Nahezu jede moderne Programmiersprache ist eine Hochsprache, einschließlich PHP und Python. Jeden Tag werden mehr Sprachen erfunden (obwohl es jetzt wahrscheinlich genug ist), aber wie genau funktioniert Ihr Code immer noch ordnungsgemäß, wenn Computer Maschinencode benötigen?
Hier kommt die Kompilierung ins Spiel. Ein Compiler ist ein Programm, das Ihren übergeordneten Code in ein Formular konvertiert, das ausgeführt werden kann. Dies könnte eine andere Hochsprache sein, aber normalerweise ist es die Montage. Einige Sprachen (wie Python oder Java) konvertieren Ihren Code in eine so genannte Zwischenstufe Bytecode. Dies muss zu einem späteren Zeitpunkt erneut kompiliert werden, was normalerweise auf Anforderung erfolgt, beispielsweise wenn das Programm ausgeführt wird. Dies ist bekannt als gerade rechtzeitig Zusammenstellung, und es ist sehr beliebt.
Speicherverwaltung
Nun, da Sie 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 zur Darstellung von Konzepten anstelle von exakter Syntax verwendet wird. Heute wird dies größtenteils C ++ ähneln, da dies (meiner Meinung nach) die beste Hochsprache ist..
In diesem Abschnitt wird es hilfreich sein, wenn Sie wissen, wie RAM funktioniert. Eine Kurzanleitung für RAM: Was Sie wissen müssen Eine Kurzanleitung für RAM: Was Sie wissen müssen Arbeitsspeicher RAM ist eine wichtige Komponente jedes Computers , aber es kann verwirrend sein zu verstehen, wenn Sie kein Tech-Guru sind. In diesem Beitrag gliedern wir es leicht verständlich auf. Weiterlesen .
Die meisten Sprachen haben Variablen - Container, die einige Daten speichern. 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 meineNummer;
Dieser Code deklariert eine aufgerufene Variable meine Nummer, und gibt ihm einen Datentyp von ganze Zahl. Nach dem Kompilieren interpretiert der Computer diesen Befehl als:
“Suchen Sie nach leerem Speicher und reservieren Sie einen Platz, der groß genug ist, um eine ganze Zahl zu speichern”
Nachdem dieser Befehl ausgeführt wurde, kann dieses Speicherbit nicht von einem anderen Programm verwendet werden. Es enthält noch keine Daten, ist aber für Ihre myNumber-Variable reserviert.
Weisen Sie Ihrer Variablen jetzt einen Wert zu:
myNumber = 10;
Um diese Aufgabe abzuschließen, greift Ihr Computer auf seinen reservierten Speicherort zu und ändert den dort gespeicherten Wert in diesen neuen Wert.
Nun, das ist alles gut und gut, aber wie werden Speicherplätze uneingeschränkt reserviert? Wenn Programme den gesamten Speicher, den sie möchten, reserviert haben, würde der RAM sofort voll sein - das würde für einen Speicherplatz sorgen sehr langsames System.
Um dieses potenzielle Problem zu vermeiden, implementieren viele Sprachen a Müllsammler, werden verwendet, um Variablen zu löschen (und damit die reservierten Speicherplätze freizugeben) außer Reichweite.
Sie fragen sich vielleicht, welchen Spielraum hat und warum es so wichtig ist. Der Bereich definiert die Grenzen und die Lebensdauer von Variablen oder den von einem Programm verwendeten Speicher. Eine Variable ist “außer Reichweite” wenn auf keinen Code mehr zugegriffen werden kann (dann tritt der Speicherbereiniger ein). Hier ist ein Beispiel:
Funktion maths () int firstNumber = 1; int secondNumber = 2; print (erste Nummer + zweite Nummer); // wird nicht funktionieren
Dieses Beispiel wird nicht kompiliert. Die Variable ersteNummer ist innerhalb der Mathe Funktion, das ist es ist Umfang. Es kann nicht von außerhalb der Funktion, in der es deklariert wurde, zugegriffen werden. Dies ist ein wichtiges Programmierkonzept, und das Verstehen ist wichtig für die Arbeit mit Zeigern.
Diese Art der Handhabung des Speichers heißt Stapel. So funktioniert die große Mehrheit der Programme. Sie müssen keine Zeiger verstehen, um sie verwenden zu können, und es ist ziemlich gut strukturiert. Der Nachteil des Stapels ist die Geschwindigkeit. Da der Computer Speicher zuweisen, die Variablen verfolgen und die Speicherbereinigung ausführen muss, ist ein geringer Aufwand erforderlich. Dies ist gut für kleinere Programme, aber was ist mit Hochleistungsaufgaben oder datenintensiven Anwendungen??
Geben Sie ein: Zeiger.
Zeiger
Auf der Oberfläche klingen Zeiger einfach. Sie referenzieren (zeigen auf) eine Stelle im Speicher. Dies scheint nicht anders zu sein “regulär” Variablen auf dem Stapel, aber vertrauen Sie mir, es gibt einen großen Unterschied. Zeiger werden im gespeichert Haufen. Dies ist das Gegenteil des Stapels - er ist weniger organisiert, aber viel schneller.
Schauen wir uns an, wie Variablen auf dem Stack zugewiesen werden:
int numberOne = 1; int numberTwo = numberOne;
Dies ist eine einfache Syntax. Die Variable Nummer zwei enthält die Nummer eins. Ihr Wert wird während der Zuweisung aus dem übernommen Nummer Eins Variable.
Wenn Sie die bekommen wollten Speicheradresse Für eine Variable müssen Sie anstelle des 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 1 an den eigenen, neuen Speicherplatz zu kopieren. Wenn Sie diese Variable ausgeben würden, wäre dies nicht die Nummer Eins (obwohl diese im Speicherplatz gespeichert ist). Es würde seinen Speicherort ausgeben (wahrscheinlich etwas wie 2167, obwohl dies je nach System und verfügbarem RAM variiert). Um auf den in einem Zeiger gespeicherten Wert zuzugreifen, müssen Sie anstelle des Speicherplatzes Dereferenzierung der Zeiger Dadurch wird direkt auf den Wert zugegriffen, der in diesem Fall die Nummer Eins wäre. So deneferenzieren Sie einen Zeiger:
int numberTwo = * numberOne;
Das Dereferenzierungsoperator ist ein Stern (*).
Dies kann ein schwer verständliches Konzept sein, also gehen wir es noch einmal durch:
- Das Adresse von 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 die Art des Zeigers selbst.
Jetzt, wo Sie wissen, was Zeiger sind, können Sie mit ihnen ein paar wirklich nette Tricks machen! Wenn Speicher verwendet wird, startet Ihr Betriebssystem der Reihe nach. Sie können RAM als Taubenlöcher vorstellen. Viele Löcher, um etwas zu speichern, nur eine kann gleichzeitig verwendet werden. Der Unterschied hier ist, dass diese Taubenlöcher alle nummeriert sind. Beim Zuweisen von Speicher startet Ihr Betriebssystem mit der niedrigsten Nummer und arbeitet. Es wird nie zwischen den Zufallszahlen springen.
Wenn Sie bei der Arbeit mit Zeigern ein Array zugewiesen haben, können Sie durch einfaches Inkrementieren des Zeigers zum nächsten Element navigieren.
Hier wird es interessant. Wenn Sie Werte an eine Funktion übergeben (mithilfe von im Stapel gespeicherten Variablen), werden diese Werte in Ihre Funktion kopiert. Wenn es sich um große Variablen handelt, speichert das Programm sie jetzt zweimal. Wenn Ihre Funktion abgeschlossen ist, benötigen Sie möglicherweise eine Möglichkeit, diese Werte zurückzugeben. Funktionen können im Allgemeinen nur eine Sache zurückgeben - wenn Sie also zwei, drei oder vier Dinge zurückgeben möchten?
Wenn Sie einen Zeiger auf Ihre Funktion übergeben, wird nur die Speicheradresse kopiert (was winzig ist). Das spart Ihrer CPU viel Arbeit! Möglicherweise zeigt Ihr Zeiger auf ein großes Bildfeld - Ihre Funktion kann nicht nur mit den gleichen Daten arbeiten, die am selben Speicherort gespeichert sind. Sobald dies geschehen ist, müssen Sie nichts zurückgeben. Ordentlich!
Sie müssen jedoch sehr vorsichtig sein. Zeiger können immer noch außer Reichweite sein 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 der Speicher ist noch belegt. Dies ist ein häufiger Grund für viele Programme, die abstürzen können, und es kann bei großen Datenmengen spektakulär ausfallen. In den meisten Fällen wird Ihr Betriebssystem Ihr Programm beenden, wenn Sie ein großes Leck haben (mit mehr RAM als das System). Dies ist jedoch nicht wünschenswert.
Das Debuggen von Zeigern kann ein Albtraum sein, insbesondere wenn Sie mit großen Datenmengen arbeiten oder in Schleifen arbeiten. Ihre Nachteile und ihre Schwierigkeit zu 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, empfehle ich C ++ in 24 Stunden.
Wenn dies ein wenig komplex war, werfen Sie einen Blick auf unseren Leitfaden zu den einfachsten Programmiersprachen. 6 Einfachste Programmiersprachen für Anfänger 6 Einfache Programmiersprachen für Anfänger Beim Programmieren lernen Sie, die richtige Sprache zu finden, genauso wie die Erbauungsprozess. Hier sind die sechs einfachsten Programmiersprachen für Anfänger. Weiterlesen .
Hast du 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 mit!
Erfahren Sie mehr über: Programmierung.