[Powered by Google Translate] [Pointers]
[Rob Bowden] [Harvard University]
[Dies ist CS50] [CS50.TV]
>> Lassen Sie uns über Hinweise sprechen.
Bis jetzt haben wir immer nur um die Dinge in Erinnerung explizit beim Namen genannt.
Wir haben gesagt, int n = 42, und dann, wenn wir die Variable n verwenden möchten,
wir nennen es einfach unter dem Namen wir ihm schriftlich etwas wie n * 2.
Aber das Variable muss irgendwo im Speicher zu leben.
Wenn Sie den Wert, der zurzeit im n gespeichert ist zu verwenden,
oder aktualisieren Sie den Wert, den n hält,
Ihr Programm muss wissen, wo im Speicher für n aussehen.
Wo im Speicher eine Variable Leben nennt seine Adresse.
Es ist wie ein Haus-Adresse.
Ich kann jemandem zu Hause finden, solange ich ihre Wohnadresse kennen,
und ein Computerprogramm, das eine variable, solange es seine Speicheradresse kennt finden.
Was Zeiger bereitzustellen ist eine Möglichkeit der direkten Umgang mit diesen Speicheradressen.
>> Ein großer Teil der Macht in C kommt von der Lage in den Speicher wie diese zu manipulieren.
Aber mit großer Macht kommt große Verantwortung.
Zeiger können gefährlich genug verwendet werden, dass eine Menge von Programmiersprachen
verbergen Zeigern ganz.
Also, warum C geben uns Hinweise dann?
Wie Sie wissen, Argumente für eine C-Funktion
immer in die Parameter der Funktion kopiert.
So rief etwas wie Swap auf einigen Variablen x und y
können nicht vertauscht werden die Werte von x und y in der aufrufenden Funktion,
obwohl das könnte nützlich sein.
Wie wir später sehen werden, Umschreiben Swap Zeiger auf die Standorte zu müssen getauscht werden dauern
ermöglicht es, ihre Anrufers Variablen beeinflussen.
>> Lassen Sie uns durch eine relativ einfache Beispiel dafür, was Zeiger können nicht gehen.
Lassen Sie uns sagen, wir haben int n = 4 und int * pointer_to_n = & n.
Whoa! Ein bisschen neue Syntax zu decken.
Lassen Sie uns zuerst interpretieren dies & n.
Beachten Sie, dass alles, was im Speicher eine Adresse hat.
Das kaufmännische wird als "Adresse"-Operator.
So bezieht & n auf die Adresse im Speicher, wo n gespeichert ist.
Nun sind wir Speichern Sie diese Adresse in eine neue Variable, pointer_to_n.
Was ist die Art dieser neuen Variablen?
Der Stern ist ein Teil der Variablen-Typ,
und wir lesen den Typ als int *.
Int * bedeutet, dass pointer_to_n eine Variable, die die Adresse eines ganzzahligen speichert ist.
Wir wissen, dass & n eine int * ist seit n eine ganze Zahl, und wir nehmen die Adresse des n.
Int * ist ein Beispiel für ein Zeigertyp.
Sobald Sie sehen Sternchen in der Art zu starten,
Sie wissen, dass Sie mit Zeigern zu tun haben.
Genau wie wir können eine Variable als int x und y char zu deklarieren,
können wir sagen, int * z und char * w.
Int * und char * sind nur neue Arten für uns zu nutzen.
Die Lage des * kann überall hingehen vor dem Variablennamen.
Also, sowohl int * pointer_to_n - mit dem * next int, wie wir hier haben -
und int * pointer_to_n mit dem * neben pointer_to_n gültig sind.
Aber hier werde ich legen Sie die * neben int.
Es spielt keine Rolle, welche Sie bevorzugen, nur konsequent sein.
>> Ziehen wir ein Diagramm für diese.
Wir müssen zuerst die Variable n, die wir als eine kleine Schachtel mit dem Gedächtnis zeichnen werde.
Für dieses Beispiel, sagen wir, dass dieses Feld an der Adresse 100 befindet.
Innerhalb von dieser Box, wir speichern den Wert 4.
Jetzt haben wir eine neue Variable, pointer_to_n.
Es hat seine eigene Box in Erinnerung,
was wir sagen, ist an der Adresse 200.
Innerhalb dieses Feldes sind wir Speichern der Adresse n,
denen wir zuvor gesagt war 100.
Häufig in Diagrammen, werden Sie sehen dies als eine wörtliche Pfeil dargestellt
Verlassen der pointer_to_n box zeigt auf das Feld, das n speichert.
Nun, was können wir tatsächlich mit pointer_to_n tun?
Nun, wenn wir so etwas wie * pointer_to_n = 8 sagen, dies ist eine andere Verwendung für das Sternchen
das ist völlig unabhängig von der Verwendung des Sterns in der Deklaration einer Variablen
eines Zeigers Typ.
Hier wird das Sternchen als Dereferenzierungsoperator.
In unserem Diagramm, was * pointer_to_n = 8 Mittel ist,
gehen Sie zu dem Feld mit pointer_to_n, folgen Sie dem Pfeil,
und anschließend in die Schachtel zuordnen am Ende des Pfeils der Wert 8.
Dies bedeutet, dass nach dieser Zeile, wenn wir versuchen, mit n wird es den Wert 8 haben.
Das Wort 'Zeiger' wird in vielen verschiedenen Zusammenhängen verwendet.
Hier werden wir versuchen, konsequent zu sein.
Ein Zeiger-Typ ist so etwas wie int *.
In diesem Video wird ein Zeiger nur verwendet werden, um einen Wert mit einer Pointer-Typ verstanden werden,
wie pointer_to_n die den Typ int *.
Überall, wo wir nur sagen n verwendet werden, können wir nun stattdessen sagen * pointer_to_n,
und alles wird genauso gut funktionieren.
>> Lassen Sie uns durch ein anderes einfaches Beispiel zu gehen.
Lassen Sie uns sagen, wir haben int n = 14; int * pointer = &n; n + +, und (* Zeiger) + +.
Die erste Zeile erzeugt eine neue Box im Speicher bezeichnet n.
Dieses Mal werden wir nicht beschriften Sie die Box mit einer expliziten Adresse, aber es hat immer noch ein.
Im Inneren der Box, wir Speichern der Nummer 14.
Die nächste Zeile erstellt eine zweite Kontrollkästchen Zeiger.
Und innerhalb dieser Box, wir speichern einen Zeiger auf das Feld n.
Also, lasst uns ziehen den Pfeil von Zeiger auf n.
Nun n + + inkrementiert den Wert in das Feld n,
so dass wir 14 bis 15 gehen.
Schließlich (* Zeiger) + + geht auf das Feld Zeiger,
dereferences der Wert in der Box, die folgen Sie dem Pfeil, wo es Punkte bedeutet,
und inkrementiert den Wert dort gespeichert, so dass wir von 15 bis 16 gehen.
Und das ist es.
N speichert nun die Nummer 16, nachdem sie bereits zweimal erhöht -
einmal direkt über die Variablennamen n, und die andere durch eine pointer_to_n.
>> Schnelle Quiz. Was bedeutet es wohl, wenn ich so etwas wie && sagen versuchen n?
Nun, lassen Sie dies als & (& n), die die gleiche Sache erreicht umzuschreiben.
Die (& n) gibt die Adresse der Variablen n im Speicher.
Aber dann anschließend äußeren kaufmännisches versucht, die Adresse der Adresse zurück.
Das ist wie zu tun versuchen & 2.
Es macht keinen Sinn, um die Adresse nur eine Zahl zu erhalten
da es nicht im Speicher abgelegt.
Mit zwei kaufmännische in einer Reihe ist nie die richtige Idee.
Aber jetzt, was bedeutet es, wenn ich int ** double_pointer = & Zeiger sagen versuchen?
Nun, ich bin ein neues Feld mit der Bezeichnung double_pointer,
und innerhalb dieser Box Ich bin Speichern der Adresse des Zeigers,
was bedeutet, dass ich einen Pfeil aus dem double_pointer box auf den Zeiger angezeigt.
Beachten Sie die Art der double_pointer, ein int **.
N war eine ganze Zahl, Zeiger gespeichert die Adresse n, und so hat es den Typ int *.
Nun speichert double_pointer die Adresse des Zeigers, so hat es den Typ int **.
>> Also, was wir denken, bedeutet dies -
** Double_pointer = 23?
Beachten Sie, dass ich jetzt Dereferenzierung zweimal.
Befolgen Sie einfach die Box-and-Pfeil-Diagramm haben wir bereits eingerichtet haben.
Zuerst gehen wir in das Feld double_pointer.
Der erste * bedeutet folgen Sie dem Pfeil einmal.
Jetzt sind wir an der Box mit einem Zeiger.
Der zweite Stern, sagt folgen erneut auf den Pfeil,
und jetzt sind wir an der Box mit n, und wir setzen den Wert dieser Box bis 23.
Feststellen, dass die dereference und "Adresse" Betreiber Inversen voneinander.
Dies ermöglicht es mir, so etwas wie * & * & n = 42 zu tun.
Während dies funktioniert, sollten Sie nie etwas tun, wie dies in der Praxis.
Was machen wir eigentlich hier?
Der erste kaufmännische packt die Adresse der Variablen n.
Dann haben wir eine Dereferenzierungsoperator, die wir an diese Adresse gehen im Speicher bedeutet,
so sind wir wieder bei n.
Jetzt greifen wir die Adresse von n immer sofort Dereferenzierung,
so sind wir wieder bei n und speichern 42.
Also, jedes Paar von * & just aufhebt.
>> Es gibt einen speziellen Zeiger namens der Null-Zeiger.
Dies ist ein Zeiger, dass wir nie dereferenzieren.
Solch ein Zeiger ist wichtig, weil sie uns einen Weg zu unterscheiden zwischen
ein Zeiger, und sollte nicht aufgelöst werden.
Wenn Sie versuchen, Dereferenzierung eines Null-Zeiger,
typischerweise Ihr Programm mit einem Segmentation Fault abstürzen,
die Sie vielleicht schon einmal gesehen zu haben.
Also, lasst uns sagen, wir haben den Code int * x = null; * x = 4.
In diesem Beispiel kann es scheint offensichtlich, dass wir etwas Schlechtes tut,
aber denken Sie daran, dass null könnte wirklich ein Wert von einem Aufruf einer Funktion zurückgegeben werden
wie malloc ist, wenn malloc kann den Speicher vom Benutzer angeforderten zuzuteilen.
Aus diesem Grund, wenn wir stattdessen den Wert von x von einem Aufruf von malloc,
wie in int * x = malloc (sizeof (int)), dann sollten wir uns immer explizit zu überprüfen
zu sehen, ob null zurückgegeben wurde.
If (x == null) / / uhoh! return; sonst können wir weiter auf und sagen * x = 4.
>> Also, noch einmal, warum sollten wir jemals benutzen Zeigern?
Lassen Sie uns ein Beispiel für ein Programm, wo wir brauchen, um Zeiger zu verwenden aussehen -
ein einfacher Swap-Funktion.
Lassen Sie uns sagen, ich habe zwei Zahlen, int x = 4, und int y = 15;
und ich möchte eine Funktion namens Swap, dass ich wie so verwenden, schreiben: swap (x, y).
Nach dieser Zeile sind die Werte im Inneren der Variablen x 15 sein,
und der Wert innerhalb variable y sollte 4.
Die Werte innerhalb von x und y sind vertauscht.
Ohne Zeiger, könnten wir so etwas wie void swap versuchen (int a, int b);
int tmp = b, b = a; a = tmp.
Aber bemerken Sie das Problem mit diesen?
Beachten Sie, dass der Wert in der Variable gespeichert a nur eine Kopie des Wertes von x,
und der Wert von b ist von y kopiert.
Alle Änderungen an a und b nicht in x und y reflektiert.
Während also die Werte von a und b vertauscht sind korrekt,
x und y sind überhaupt nicht verändert.
Jetzt ändern wir die Swap-Funktion, so dass ihre Argumente
sind Zeiger auf die Variablen, die es tauschen, möchte so:
void swap (int * a, int * b); int tmp = * b, * b = * a; * a = tmp.
Beachten Sie, dass Swaps Argumente sind nun Zeiger,
und so müssen wir die Adresse von x und y in dem Aufruf zu tauschen, wie so übergeben:
swap (& x, & y).
Dies nun korrekt vertauscht die Werte von x und y.
>> Ziehen wir eine Box-and-Pfeil-Diagramm, warum diese Werke zu sehen.
Wir beginnen mit unseren beiden Boxen in dem Speicher, x und y.
Innere des Kastens für x die Zahl 4 und Innenraum des Kastens für y haben wir 15.
Jetzt, innerhalb der Aufruf an die Swap-Funktion, haben wir zwei weitere Boxen
für die Argumente a und b;
ein Zug an der Box für x und b zeigt auf der Box für y.
Ein neues Feld für die Variable tmp erstellt,
und nach innen von ihm speichern wir das Ergebnis der Dereferenzierung b,
was bedeutet, "folgen Sie dem Pfeil aus dem Feld mit der Bezeichnung b.
So speichern wir 15 innerhalb des tmp.
Dann folgen wir den Pfeil b und speichern Sie hier das Ergebnis der Dereferenzierung a,
das ist der Wert 4.
Schließlich folgen wir dem Pfeil auf ein und speichern, was derzeit innerhalb tmp ist die 15.
Beachten Sie, dass die Kästen markiert x und y Werte korrekt vertauscht.
>> Sobald wir mehr über malloc und dynamische Speicherverwaltung,
wir werden sehen, dass wir keine andere Wahl haben, als Zeiger verwenden.
Ein Spaziergang durch die Box-and-Pfeil-Diagramm für jedes Programm
kann Ihnen helfen, herauszufinden, was das Programm wirklich tut.
>> Mein Name ist Rob Bowden, und dies ist CS50.
[CS50.TV]
>> Dies ist eine andere Verwendung für die asterisk - bleah, ich hasse dieses Wort.
Überall, wo wir nur sagen n verwendet werden, können wir jetzt sagen pointer_to_n - nein, du kann nicht - * pointer_to_n.