Ich habe diesem Tutorial gefolgt, wie ein Zeiger auf einen Zeiger funktioniert funktioniert.
Lassen Sie mich die relevante Passage zitieren:
int i = 5, j = 6, k = 7; int *ip1 = &i, *ip2 = &j;
Jetzt können wir einstellen
int **ipp = &ip1;
und
ipp
zeigt aufip1
welche zeigt aufi
.*ipp
istip1
, und**ipp
isti
oder 5. Wir können die Situation mit unserer bekannten Box-Pfeil-Notation wie folgt veranschaulichen:Wenn dann sagen wir
*ipp = ip2;
Wir haben den Zeiger, auf den
ipp
(das heißtip1
) zeigtip2
, so geändert , dass er eine Kopie von enthält , sodass er (ip1
) jetzt auf Folgendes zeigtj
:
Meine Frage ist: Warum zeigt im zweiten Bild immer ipp
noch, ip1
aber nicht ip2
?
ipp
bei der Definition hinzu, damit Ihre Frage vollständig ist ;-)ipp
zu zeigen ,ip1
so*ipp = ip2
ist die gleiche wieip1 = ip2
int **ipp
ist weit weniger intuitiv alsint** ipp
, insbesondere wenn es sich um**ipp
eine völlig andere Sache außerhalb von Typdeklarationen handelt.int **ipp
scheint mir ziemlich intuitiv zu sein. Es bedeutet, ich mache**ipp
eineint
. Was wahr ist.ipp
istint**
, also schreiben Sie einfachint**
statt magischer "imp
Dereferenzierung ist ein int" Verständnis .Antworten:
Vergessen Sie für eine Sekunde die Zeigeanalogie. Was ein Zeiger wirklich enthält, ist eine Speicheradresse. Dies
&
ist der Operator "Adresse von", dh er gibt die Adresse im Speicher eines Objekts zurück. Der*
Operator gibt Ihnen das Objekt an, auf das sich ein Zeiger bezieht, dh wenn ein Zeiger eine Adresse enthält, gibt er das Objekt an dieser Speicheradresse zurück. Wenn Sie dies tun*ipp = ip2
, rufen Sie*ipp
das Objekt an der Adresse ab, inipp
der es sich befindet,ip1
und weisen es dannip1
dem Wert zu, inip2
dem sich die Adresse befindetj
.Einfach
&
-> Adresse von*
-> Wert beiquelle
Weil Sie den Wert geändert haben, auf den
ipp
nicht der Wert von zeigtipp
. Zeigt alsoipp
immer noch aufip1
(den Wert vonipp
),ip1
ist der Wert jetzt der gleiche wieip2
der Wert von, also zeigen beide aufj
.Dies:
ist das gleiche wie:
quelle
int *ip1 = &i
und hinzuweisen*ipp = ip2;
, dh wenn Sie dieint
Anweisung aus der ersten Anweisung entfernen, sehen die Zuweisungen sehr ähnlich aus, aber*
in beiden Fällen geschieht etwas ganz anderes.Wie die meisten Anfängerfragen im C-Tag kann diese Frage beantwortet werden, indem auf die ersten Prinzipien zurückgegriffen wird:
&
Operator verwandelt eine Variable in einen Zeiger.*
Operator verwandelt einen Zeiger in eine Variable.(Technisch sollte ich "lvalue" anstelle von "variable" sagen, aber ich denke, es ist klarer, veränderbare Speicherorte als "Variablen" zu beschreiben.)
Wir haben also Variablen:
Variable
ip1
enthält einen Zeiger. Der&
Operator verwandelt sichi
in einen Zeiger und dieser Zeigerwert wird zugewiesenip1
. Soip1
enthält einen Zeiger aufi
.Variable
ip2
enthält einen Zeiger. Der&
Operator verwandelt sichj
in einen Zeiger und dieser Zeiger wird zugewiesenip2
. Soip2
enthält einen Zeiger aufj
.Variable
ipp
enthält einen Zeiger. Der&
Operator verwandelt eine Variableip1
in einen Zeiger und dieser Zeigerwert wird zugewiesenipp
. Soipp
enthält einen Zeiger aufip1
.Fassen wir die bisherige Geschichte zusammen:
i
enthält 5j
enthält 6ip1
enthält "Zeiger aufi
"ip2
enthält "Zeiger aufj
"ipp
enthält "Zeiger aufip1
"Jetzt sagen wir
Der
*
Operator wandelt einen Zeiger wieder in eine Variable um. Wir holen den Wert vonipp
, der "Zeiger auf" ist,ip1
und verwandeln ihn in eine Variable. Welche Variable?ip1
Natürlich!Daher ist dies einfach eine andere Art zu sagen
Also holen wir den Wert von
ip2
. Was ist es? "Zeiger aufj
". Wir weisen diesen Zeigerwert zuip1
, alsoip1
ist er jetzt "Zeiger aufj
".Wir haben nur eines geändert: den Wert von
ip1
:i
enthält 5j
enthält 6ip1
enthält "Zeiger aufj
"ip2
enthält "Zeiger aufj
"ipp
enthält "Zeiger aufip1
"Eine Variable ändert sich, wenn Sie sie zuweisen. Zähle die Aufgaben; Es kann nicht mehr Änderungen an Variablen geben als Zuweisungen! Sie beginnen mit der Zuordnung zu
i
,j
,ip1
,ip2
undipp
. Sie weisen dann zu*ipp
, was, wie wir gesehen haben, dasselbe bedeutet wie "zuweisen zuip1
". Da Sie keinipp
zweites Mal zugewiesen haben, hat sich nichts geändert!Wenn Sie sich ändern
ipp
möchten, müssen Sie tatsächlich Folgendes zuweisenipp
:zum Beispiel.
quelle
Ich hoffe, dieser Code kann helfen.
es gibt aus:
quelle
Meine ganz persönliche Meinung ist, dass Bilder mit Pfeilen in diese Richtung zeigen oder dass Zeiger schwerer zu verstehen sind. Es lässt sie wie abstrakte, mysteriöse Wesen erscheinen. Sie sind nicht.
Wie alles in Ihrem Computer sind Zeiger Zahlen . Der Name "Zeiger" ist nur eine ausgefallene Art zu sagen "eine Variable, die eine Adresse enthält".
Lassen Sie mich deshalb die Dinge aufrühren, indem ich erkläre, wie ein Computer tatsächlich funktioniert.
Wir haben eine
int
, es hat den Nameni
und den Wert 5. Dies ist im Speicher gespeichert. Wie alles, was im Speicher gespeichert ist, benötigt es eine Adresse, sonst könnten wir sie nicht finden. Nehmen wir an, eri
landet an der Adresse 0x12345678 und sein Freundj
mit dem Wert 6 endet direkt danach. Angenommen, eine 32-Bit-CPU mit int 4 Bytes und Zeigern 4 Bytes, werden die Variablen wie folgt im physischen Speicher gespeichert:Nun wollen wir auf diese Variablen zeigen. Wir erstellen einen Zeiger auf int ,,
int* ip1
und einenint* ip2
. Wie alles im Computer werden auch diese Zeigervariablen irgendwo im Speicher zugeordnet. Nehmen wir an, sie landen unmittelbar danach an den nächsten benachbarten Adressen im Speicherj
. Wir setzen die Zeiger so, dass sie die Adressen der zuvor zugewiesenen Variablen enthalten:ip1=&i;
("kopiere die Adresse von i in ip1") undip2=&j
. Was zwischen den Zeilen passiert, ist:Was wir also bekamen, waren noch einige 4-Byte-Speicherblöcke, die Zahlen enthielten. Es sind keine mystischen oder magischen Pfeile in Sicht.
Wenn wir uns nur einen Speicherauszug ansehen, können wir nicht sagen, ob die Adresse 0x12345680 ein
int
oder enthältint*
. Der Unterschied besteht darin, wie unser Programm die unter dieser Adresse gespeicherten Inhalte verwendet. (Die Aufgabe unseres Programms besteht eigentlich nur darin, der CPU mitzuteilen, was mit diesen Zahlen zu tun ist.)Dann fügen wir noch eine weitere Indirektionsebene mit hinzu
int** ipp = &ip1;
. Wieder bekommen wir nur einen Teil der Erinnerung:Das Muster kommt mir bekannt vor. Noch ein Teil von 4 Bytes, der eine Zahl enthält.
Wenn wir nun einen Speicherauszug des obigen fiktiven kleinen RAM hätten, könnten wir manuell überprüfen, wohin diese Zeiger zeigen. Wir schauen uns an, was unter der Adresse der
ipp
Variablen gespeichert ist, und finden den Inhalt 0x12345680. Welches ist natürlich die Adresse, woip1
gespeichert wird. Wir können zu dieser Adresse gehen, den Inhalt dort überprüfen und die Adresse von findeni
, und schließlich können wir zu dieser Adresse gehen und die Nummer 5 finden.Wenn wir also den Inhalt von ipp nehmen, erhalten
*ipp
wir die Adresse der Zeigervariablenip1
. Durch das Schreiben*ipp=ip2
kopieren wir ip2 in ip1, es entsprichtip1=ip2
. In jedem Fall würden wir bekommen(Diese Beispiele wurden für eine Big-Endian-CPU angegeben.)
quelle
location, value, variable
in der1,2,3,4,5
angegeben wurdeA,1,B,C,3
, wo sich Position und Wert befanden, konnte die entsprechende Idee von Zeigern leicht erklärt werden, ohne dass Pfeile verwendet wurden, die von Natur aus verwirrend sind. Unabhängig von der gewählten Implementierung ist an einer bestimmten Stelle ein Wert vorhanden, und dies ist ein Teil des Puzzles, der beim Modellieren mit Pfeilen verschleiert wird.&
Operator für eine Variable gibt Ihnen eine Münze, die diese Variable darstellt. Der*
Operator auf dieser Münze gibt Ihnen die Variable zurück. Keine Pfeile erforderlich!Beachten Sie die Aufgaben:
Ergebnisse
ipp
zu zeigenip1
.Um
ipp
darauf hinzuweisenip2
, sollten wir uns auf ähnliche Weise ändern:was wir eindeutig nicht tun. Stattdessen ändern wir den Wert an der Adresse, auf die verwiesen wird
ipp
.Durch das Folgende
Wir ersetzen nur den in gespeicherten Wert
ip1
.ipp = &ip1
bedeutet*ipp = ip1 = &i
,jetzt ,
*ipp = ip2 = &j
.Also,
*ipp = ip2
ist im Wesentlichen das gleiche wieip1 = ip2
.quelle
Keine spätere Zuordnung hat den Wert von geändert
ipp
. Deshalb weist es immer noch darauf hinip1
.Was Sie damit tun
*ipp
, dh mitip1
, ändert nichts an der Tatsache, dassipp
darauf hingewiesen wirdip1
.quelle
Wenn du schöne Bilder platziert hast, werde ich versuchen, schöne ASCII-Kunst zu machen:
Wie @ Robert-S-Barnes in seiner Antwort sagte: Vergessen Sie Zeiger und was auf was hinweist, aber denken Sie in Bezug auf das Gedächtnis. Grundsätzlich
int*
bedeutet ein , dass es die Adresse einer Variablenint**
enthält und ein die Adresse einer Variablen enthält, die die Adresse einer Variablen enthält. Dann können Sie die Algebra des Zeigers verwenden, um auf die Werte oder Adressen zuzugreifen:&foo
meansaddress of foo
und*foo
meansvalue of the address contained in foo
.Da es bei Zeigern um den Umgang mit dem Gedächtnis geht, besteht der beste Weg, dies tatsächlich "greifbar" zu machen, darin, zu zeigen, was die Zeigeralgebra mit dem Gedächtnis tut.
Hier ist also der Speicher Ihres Programms (vereinfacht für den Zweck des Beispiels):
wenn Sie Ihren Anfangscode machen:
So sieht dein Gedächtnis aus:
dort können Sie sehen ,
ip1
undip2
die Adressen bekommeni
undj
undipp
immer noch nicht existiert. Vergessen Sie nicht, dass Adressen einfach Ganzzahlen sind, die mit einem speziellen Typ gespeichert sind.Dann deklarieren und definieren Sie
ipp
wie:Also hier ist deine Erinnerung:
und dann ändern Sie den Wert, auf den die Adresse zeigt, in
ipp
der die Adresse gespeichert istip1
:Der Speicher des Programms ist
NB:
int*
Da es sich um einen speziellen Typ handelt, ziehe ich es vor, immer zu vermeiden, mehrere Zeiger in derselben Zeile zu deklarieren, wie ich dasint *x;
oder denkeint *x, *y;
Notation irreführend sein kann. Ich schreibe lieberint* x; int* y;
HTH
quelle
ip2
soll3
nicht4
.Denn wenn du sagst
Sie sagen das 'Objekt, auf das gezeigt wird
ipp
', um die Richtung des Gedächtnisses anzuzeigen,ip2
das zeigt.Du sagst nicht
ipp
zu zeigenip2
.quelle
Wenn Sie den Dereferenzierungsoperator hinzufügen
*
dem Zeiger , leiten Sie vom Zeiger zum Objekt weiter, auf das gezeigt wird.Beispiele:
Deshalb:
quelle
Wenn Sie wollen , würde
ipp
zu zeigenip2
, würden Sie zu sagen habenipp = &ip2;
. Dies würde jedochip1
immer noch darauf hinweiseni
.quelle
Ganz am Anfang hast du gesetzt,
Jetzt dereferenziere es als,
quelle
Betrachten Sie jede Variable wie folgt dargestellt:
Ihre Variablen sollten also so dargestellt werden
Wie der Wert von
ipp
ist&ip1
so die Anweisung:ändert den Wert an der Addess
&ip1
in den Wert vonip2
, was bedeutet, dassip1
geändert wird:Aber
ipp
trotzdem:Der Wert von
ipp
still zeigt&ip1
also immer noch aufip1
.quelle
Weil Sie den Zeiger von ändern
*ipp
. Es bedeutetipp
(variabler Name) ---- geh rein.ipp
ist die Adresse vonip1
.*ipp
geh also zu (adresse von innen)ip1
.Jetzt sind wir bei
ip1
.*ipp
(ieip1
) =ip
2.ip2
Adresse von.j
enthaltenip1
Inhalt wird durch Inhalt von ip2 (dh Adresse von j) ersetzt, WIR ÄNDERN DEN INHALT NICHTipp
. DAS IST ES.quelle
*ipp = ip2;
impliziert:Weisen Sie
ip2
der Variablen zu, auf die von zeigtipp
. Das entspricht also:Wenn Sie möchten, dass die Adresse von
ip2
gespeichert wirdipp
, gehen Sie einfach wie folgt vor:Zeigt jetzt
ipp
aufip2
.quelle
ipp
kann den Wert eines Zeigers auf ein Objekt vom Zeigertyp enthalten (dh auf einen Zeiger zeigen) . Wenn Sie das tundann
ipp
enthält das die Adresse der Variablen (Zeiger)ip2
, die&ip2
vom Typ Zeiger auf Zeiger ist ( ) . Jetzt zeigt der Pfeilipp
im zweiten Bild aufip2
.Wiki sagt:
Der
*
Operator ist ein Dereferenzierungsoperator, der mit einer Zeigervariablen arbeitet und einen l-Wert zurückgibt (Variable) , der dem Wert an der Zeigeradresse entspricht. Dies wird als Dereferenzierung des Zeigers bezeichnet.Anwenden des
*
Operators aufipp
Dereferenzierung auf einen l-Wert des Zeigers auf denint
Typ. Der dereferenzierte l-Wert*ipp
ist vom Typ Zeiger aufint
, er kann die Adresse einesint
Typs Daten enthalten. Nach der Aussageipp
hält die Adresse vonip1
und*ipp
hält die Adresse von (zeigt auf)i
. Man kann sagen, das*ipp
ist ein Alias vonip1
. Beide**ipp
und*ip1
sind Alias füri
.Indem ich es tue
*ipp
undip2
beide zeigen auf dieselbe Position, zeigen aberipp
immer noch aufip1
.Was
*ipp = ip2;
tatsächlich bedeutet, ist, dass es den Inhalt vonip2
(die Adresse vonj
) nach kopiertip1
(wie*ipp
es ein Alias für istip1
), wodurch tatsächlich beide Zeiger erstellt werdenip1
undip2
auf dasselbe Objekt gezeigt wird (j
).In der zweiten Abbildung zeigt der Pfeil von
ip1
undip2
auf,j
während eripp
noch zeigt,ip1
da keine Änderung vorgenommen wird, um den Wert von zu ändernipp
.quelle