Ich lerne ein wenig C ++ und kämpfe mit Zeigern. Ich verstehe, dass ich 3 Zeigerebenen haben kann, indem ich Folgendes erkläre:
int *(*x)[5];
Das *x
ist also ein Zeiger auf ein Array von 5 Elementen, auf die Zeiger sind int
. Auch weiß ich , dass x[0] = *(x+0);
, x[1] = *(x+1)
und so weiter ....
Warum also angesichts der obigen Erklärung x[0] != x[0][0] != x[0][0][0]
?
x[0]
,x[0][0]
Undx[0][0][0]
verschiedene Typen haben. Sie können nicht verglichen werden. Was meinst du damit!=
?int **x[5]
ist ein Array von 5 Elementen. Ein Element ist ein Zeiger auf Zeiger auf int`int** x[5]
wäre ein Array von fünf Zeigern, die auf Zeiger zeigen, die auf int zeigen.int *(*x)[5]
ist ein Zeiger auf ein Array von fünf Zeigern, die auf int zeigen.x[0] != x[0][0] != x[0][0][0]
das? Dies ist kein gültiger Vergleich in C ++. Auch wenn Sie es in aufgeteilt habenx[0] != x[0][0]
undx[0][0] != x[0][0][0]
es immer noch nicht gültig ist. Was bedeutet Ihre Frage?Antworten:
x
ist ein Zeiger auf ein Array von 5 Zeigern aufint
.x[0]
ist ein Array von 5 Zeigern aufint
.x[0][0]
ist ein Zeiger auf einint
.x[0][0][0]
ist einint
.Sie können sehen, dass
x[0]
ist ein Array und wird bei Verwendung in einem Ausdruck (mit einigen Ausnahmen) in einen Zeiger auf sein erstes Element konvertiert. Daherx[0]
wird die Adresse seines ersten Elements angegeben,x[0][0]
das ist0x500
.x[0][0]
enthält die Adresse einesint
was ist0x100
.x[0][0][0]
enthält einenint
Wert von10
.Also
x[0]
ist gleich&x[0][0]
und daher&x[0][0] != x[0][0]
.Daher
x[0] != x[0][0] != x[0][0][0]
.quelle
0x100
sollte sofort links neben der Box erscheinen10
, genau wie0x500
links neben der Box. Anstatt weit links und unten zu sein.ist nach Ihrem eigenen Beitrag,
das ist vereinfacht
Warum sollte es gleich sein?
Der erste ist die Adresse eines Zeigers.
Der zweite ist die Adresse eines anderen Zeigers.
Und der dritte ist ein
int
Wert.quelle
x[0][0]
ist(x[0])[0]
also*((*(x+0))+0)
nicht*(x+0+0)
. Dereferenzierung erfolgt vor der Sekunde[0]
.x[0][0] != *(x+0+0)
genau wiex[2][3] != x[3][2]
.Hier ist das Speicherlayout Ihres Zeigers:
x[0]
ergibt "Adresse des Arrays",x[0][0]
ergibt "Zeiger 0",x[0][0][0]
ergibt "eine ganze Zahl".Ich glaube, es sollte jetzt offensichtlich sein, warum sie alle unterschiedlich sind.
Das Obige ist nah genug für das grundlegende Verständnis, weshalb ich es so geschrieben habe, wie ich es geschrieben habe. Wie Haccks jedoch zu Recht hervorhebt, ist die erste Zeile nicht 100% genau. Also hier kommen alle feinen Details:
Nach der Definition der C-Sprache ist der Wert von
x[0]
das gesamte Array von Ganzzahlzeigern. Arrays sind jedoch etwas, mit dem Sie in C eigentlich nichts anfangen können. Sie manipulieren immer entweder ihre Adresse oder ihre Elemente, niemals das gesamte Array als Ganzes:Sie können
x[0]
an densizeof
Operator übergeben. Aber das ist nicht wirklich eine Verwendung des Wertes, sein Ergebnis hängt nur vom Typ ab.Sie können die Adresse nehmen, die den Wert von ergibt
x
, dh "Adresse des Arrays" mit dem Typint*(*)[5]
. Mit anderen Worten:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
In allen anderen Kontexten
x[0]
zerfällt der Wert von in einen Zeiger auf das erste Element im Array. Das heißt, ein Zeiger mit dem Wert "Adresse des Arrays" und dem Typint**
. Der Effekt ist der gleiche, als hätten Siex
auf einen Zeiger vom Typ gewirktint**
.Aufgrund des Zerfalls des Array-Zeigers in Fall 3 führen alle Verwendungen von
x[0]
letztendlich zu einem Zeiger, der auf den Anfang des Zeiger-Arrays zeigt. Der Aufrufprintf("%p", x[0])
druckt den Inhalt der Speicherzellen, die als "Adresse des Arrays" gekennzeichnet sind.quelle
x[0]
ist nicht die Adresse des Arrays.x[0]
ist nicht die Adresse des Arrays, sondern das Array selbst. Ich habe eine ausführliche Erklärung dazu hinzugefügt und warum ich das geschrieben habe,x[0]
ist die "Adresse des Arrays". Ich hoffe du magst es.printf("%zu\n", sizeof x[0]);
Gibt die Größe des Arrays an, nicht die Größe eines Zeigers.sizeof x[0]
...x[0]
dereferenziert den äußersten Zeiger ( Zeiger auf Array der Größe 5 des Zeigers auf int) und führt zu einem Array der Größe 5 des Zeigers aufint
;x[0][0]
dereferenziert den äußersten Zeiger und indiziert das Array, was zu einem Zeiger auf führtint
;x[0][0][0]
dereferenziert alles, was zu einem konkreten Wert führt.Übrigens, wenn Sie sich jemals verwirrt fühlen, was diese Art von Erklärungen bedeutet, verwenden Sie cdecl .
quelle
Lassen Sie Schritt für Schritt Ausdrücke betrachten
x[0]
,x[0][0]
undx[0][0][0]
.Wie
x
folgt definiertdann ist expression
x[0]
ein Array vom Typint *[5]
. Berücksichtigen Sie, dass Ausdruckx[0]
gleich Ausdruck ist*x
. Das heißt, einen Zeiger auf ein Array dereferenzieren, wir erhalten das Array selbst. Bezeichne es wie y, das heißt, wir haben eine ErklärungAusdruck
x[0][0]
ist äquivalent zuy[0]
und hat Typint *
. Bezeichne es wie z, das heißt, wir haben eine DeklarationAusdruck
x[0][0][0]
ist äquivalent zu Ausdrucky[0][0]
, der wiederum äquivalent zu Ausdruck istz[0]
und Typ hatint
.Also haben wir
x[0]
hat Typint *[5]
x[0][0]
hat Typint *
x[0][0][0]
hat Typint
Sie sind also Objekte unterschiedlichen Typs und unterschiedlicher Größe.
Zum Beispiel ausführen
quelle
Als erstes muss ich das sagen
Aus dem folgenden Bild sind alle Dinge klar.
Dies ist nur ein Beispiel, bei dem der Wert von x [0] [0] [0] = 10 ist
und die Adresse von x [0] [0] [0] ist 1001
Diese Adresse wird in x [0] [0] = 1001 gespeichert
und die Adresse von x [0] [0] ist 2000
und diese Adresse wird bei x [0] = 2000 gespeichert
Also x [0] [0] [0] ≠ x [0] [0] ≠ x [0]
.
BEARBEITUNGEN
Programm 1:
Ausgabe
Programm 2:
Ausgabe
quelle
x[0]
enthält keine Ameisenadresse. Es ist ein Array. Es verfällt, um auf sein erstes Element zu zeigen.Wenn Sie die Arrays aus einer realen Perspektive betrachten würden, würde dies folgendermaßen aussehen:
x[0]
ist ein Frachtcontainer voller Kisten.x[0][0]
ist eine einzelne Kiste voller Schuhkartons im Frachtcontainer.x[0][0][0]
ist ein einzelner Schuhkarton in der Kiste, im Frachtcontainer.Auch wenn es der einzige Schuhkarton in der einzigen Kiste im Frachtcontainer war, ist es immer noch ein Schuhkarton und kein Frachtcontainer
quelle
x[0][0]
Wäre es nicht eine einzige Kiste voller Papierstücke, auf denen die Positionen der Schuhkartons stehen?In C ++ gibt es ein Prinzip: Eine Deklaration einer Variablen gibt genau an, wie die Variable verwendet wird. Betrachten Sie Ihre Erklärung:
das kann umgeschrieben werden als (zur Verdeutlichung):
Aufgrund des Prinzips haben wir:
Deshalb:
So können Sie den Unterschied herausfinden.
quelle
x[0]
ist ein Array von 5 Zoll, kein Zeiger. (In den meisten Kontexten kann es zu einem Zeiger werden, aber die Unterscheidung ist hier wichtig).*(*x)[5]
ist einint
, so(*x)[5]
ist einint *
, so*x
ist ein(int *)[5]
, sox
ist ein*((int *)[5])
. Das heißt,x
ist ein Zeiger auf ein 5-Array von Zeigern aufint
.Sie versuchen, verschiedene Typen nach Wert zu vergleichen
Wenn Sie die Adressen übernehmen, erhalten Sie möglicherweise mehr von dem, was Sie erwarten
Denken Sie daran, dass Ihre Erklärung einen Unterschied macht
erlauben würde , die Vergleichen Sie wollen, da
y
,y[0]
,y[0][0]
,y[0][0][0]
würde verschiedene Werte und Typen haben aber die gleiche Adressebelegt keinen zusammenhängenden Raum.
x
undx [0]
haben die gleiche Adresse, aberx[0][0]
undx[0][0][0]
sind jeweils an unterschiedlichen Adressenquelle
int *(*x)[5]
ist anders alsint **x[5]
Ein
p
Zeiger sein: Sie stapeln Dereferenzen mitp[0][0]
, was äquivalent zu ist*((*(p+0))+0)
.In C-Referenz (&) und Dereferenzierungsnotation (*):
Ist äquivalent zu:
Schauen Sie, das & * kann überarbeitet werden, indem Sie es einfach entfernen:
quelle
p == p
.&(&p[0])[0]
ist anders alsp[0][0]
Die anderen Antworten sind richtig, aber keine von ihnen betont die Idee, dass es möglich ist, dass alle drei den gleichen Wert enthalten , und dass sie daher in gewisser Weise unvollständig sind.
Der Grund, warum dies aus den anderen Antworten nicht verstanden werden kann, ist, dass alle Abbildungen, obwohl sie unter den meisten Umständen hilfreich und definitiv vernünftig sind, nicht die Situation abdecken, in der der Zeiger
x
auf sich selbst zeigt.Dies ist ziemlich einfach zu konstruieren, aber eindeutig etwas schwieriger zu verstehen. Im folgenden Programm werden wir sehen, wie wir erzwingen können, dass alle drei Werte identisch sind.
HINWEIS: Das Verhalten in diesem Programm ist nicht definiert, aber ich bin Entsendung es hier rein als eine interessante Demonstration von etwas , dass Zeiger können tun, aber soll nicht .
Dies wird sowohl in C89 als auch in C99 ohne Warnungen kompiliert, und die Ausgabe lautet wie folgt:
Interessanterweise sind alle drei Werte identisch. Das sollte aber keine Überraschung sein! Lassen Sie uns zunächst das Programm aufschlüsseln.
Wir deklarieren
x
als Zeiger auf ein Array von 5 Elementen, wobei jedes Element vom Typ Zeiger auf int ist. Diese Deklaration weist 4 Laufzeitbytes auf dem Laufzeitstapel zu (oder mehr, abhängig von Ihrer Implementierung; auf meinem Computer sind Zeiger 4 Bytes),x
bezieht sich also auf einen tatsächlichen Speicherort. In der C-Sprachfamilie ist der Inhalt vonx
nur Müll, etwas, das von der vorherigen Nutzung des Standorts übrig geblieben ist, sodass erx
selbst nirgendwo hinweist - schon gar nicht auf zugewiesenen Speicherplatz.Natürlich können wir die Adresse der Variablen nehmen
x
und irgendwo ablegen , also tun wir genau das. Aber wir werden weitermachen und es in x selbst setzen. Da es&x
sich um einen anderen Typ handeltx
, müssen wir eine Besetzung durchführen, damit wir keine Warnungen erhalten.Das Speichermodell würde ungefähr so aussehen:
Der 4-Byte-Speicherblock an der Adresse
0xbfd9198c
enthält also das Bitmuster, das dem Hexadezimalwert entspricht0xbfd9198c
. Einfach genug.Als nächstes drucken wir die drei Werte aus. Die anderen Antworten erklären, worauf sich jeder Ausdruck bezieht, daher sollte die Beziehung jetzt klar sein.
Wir können sehen, dass die Werte gleich sind, aber nur in einem sehr niedrigen Sinne ... ihre Bitmuster sind identisch, aber die jedem Ausdruck zugeordneten Typdaten bedeuten, dass ihre interpretierten Werte unterschiedlich sind. Wenn wir beispielsweise
x[0][0][0]
mit der Formatzeichenfolge ausdrucken%d
, erhalten wir eine große negative Zahl, sodass die "Werte" in der Praxis unterschiedlich sind, das Bitmuster jedoch dasselbe ist.Das ist eigentlich ganz einfach ... In den Diagrammen zeigen die Pfeile nur auf dieselbe Speicheradresse und nicht auf verschiedene. Obwohl wir in der Lage waren, ein erwartetes Ergebnis aus undefiniertem Verhalten zu erzwingen, ist es nur das - undefiniert. Dies ist kein Produktionscode, sondern lediglich der Vollständigkeit halber eine Demonstration.
In einer vernünftigen Situation
malloc
erstellen Sie das Array mit 5 int-Zeigern und erneut die Ints, auf die in diesem Array verwiesen wird.malloc
Gibt immer eine eindeutige Adresse zurück (es sei denn, Sie haben nicht genügend Speicher, in diesem Fall wird NULL oder 0 zurückgegeben), sodass Sie sich nie um solche selbstreferenziellen Zeiger kümmern müssen.Hoffentlich ist das die vollständige Antwort, die Sie suchen. Sie sollten nicht erwarten
x[0]
,x[0][0]
undx[0][0][0]
gleich zu sein, aber sie könnten sein, wenn sie gezwungen werden. Wenn Ihnen etwas über den Kopf ging, lassen Sie es mich wissen, damit ich es klären kann!quelle
x[0]
stellt eigentlich kein gültiges Objekt des richtigen Typs darx
ist ein Zeiger auf ein Array, daher können wir den[]
Operator verwenden, um einen Versatz von diesem Zeiger anzugeben und ihn zu dereferenzieren. Was ist dort seltsam? Das Ergebnis vonx[0]
ist ein Array, und C beschwert sich nicht, wenn Sie es mit drucken,%p
da es ohnehin so darunter implementiert ist.-pedantic
Flag erzeugt keine Warnungen, so dass C mit den Typen inDer Typ von
int *(*x)[5]
istint* (*)[5]
dh ein Zeiger auf ein Array von 5 Zeigern auf Ints.x
ist die Adresse des ersten Arrays von 5 Zeigern auf Ints (eine Adresse mit Typint* (*)[5]
)x[0]
die Adresse des ersten Arrays von 5 Zeigern auf Ints (gleiche Adresse mit Typint* [5]
) (Versatzadresse x um0*sizeof(int* [5])
dh Index * Größe des Typs, auf den gezeigt wird, und Dereferenzierung)x[0][0]
ist der erste Zeiger auf ein int im Array (dieselbe Adresse mit Typint*
) (Offset-Adresse x um0*sizeof(int* [5])
und Dereferenzierung und dann um0*sizeof(int*)
und Dereferenzierung)x[0][0][0]
ist das erste int, auf das der Zeiger auf ein int zeigt (Offset-Adresse x um0*sizeof(int* [5])
und Dereferenzierung und Offset dieser Adresse um0*sizeof(int*)
und Dereferenzierung und Offset dieser Adresse um0*sizeof(int)
und Dereferenzierung)Der Typ von
int *(*y)[5][5][5]
istint* (*)[5][5][5]
dh ein Zeiger auf ein 3D-Array von 5x5x5 Zeigern auf Intsx
ist die Adresse des ersten 3D-Arrays von 5x5x5 Zeigern auf Ints mit Typint*(*)[5][5][5]
x[0]
ist die Adresse des ersten 3D-Arrays von 5x5x5 Zeigern auf Ints (Offset-Adresse x um0*sizeof(int* [5][5][5])
und Dereferenzierung)x[0][0]
ist die Adresse des ersten 2d-Arrays von 5x5 Zeigern auf Ints (Offset-Adresse x um0*sizeof(int* [5][5][5])
und Dereferenzierung, dann Offset dieser Adresse um0*sizeof(int* [5][5])
)x[0][0][0]
ist die Adresse des ersten Arrays von 5 Zeigern auf Ints (Versatzadresse x um0*sizeof(int* [5][5][5])
und Dereferenzierung und Versatz dieser Adresse um0*sizeof(int* [5][5])
und Versatz dieser Adresse um0*sizeof(int* [5])
)x[0][0][0][0]
ist der erste Zeiger auf ein int im Array (Versatzadresse x um0*sizeof(int* [5][5][5])
und Dereferenzierung und Versatz dieser Adresse um0*sizeof(int* [5][5])
und Versatz dieser Adresse um0*sizeof(int* [5])
und Versatz dieser Adresse um0*sizeof(int*)
und Dereferenzierung)x[0][0][0][0][0]
ist das erste int, auf das der Zeiger auf ein int zeigt (Offset-Adresse x um0*sizeof(int* [5][5][5])
und Dereferenzierung und Offset dieser Adresse um0*sizeof(int* [5][5])
und Offset dieser Adresse um0*sizeof(int* [5])
und Offset dieser Adresse um0*sizeof(int*)
und Dereferenzierung und Offset dieser Adresse um0*sizeof(int)
und Dereferenzierung)Wie für Array-Zerfall:
Dies ist gleichbedeutend mit dem Bestehen
int* x[][5][5]
oderint* (*x)[5][5]
dh sie verfallen alle zu letzterem. Aus diesem Grund erhalten Sie keine Compiler-Warnung für die Verwendungx[6][0][0]
in der Funktion, dies wird jedoch der Fall sein,x[0][6][0]
da diese Größeninformationen erhalten bleibenx[0]
ist die Adresse des ersten 3D-Arrays von 5x5x5 Zeigern auf Intsx[0][0]
ist die Adresse des ersten 2d-Arrays von 5x5 Zeigern auf Intsx[0][0][0]
ist die Adresse des ersten Arrays von 5 Zeigern auf Intsx[0][0][0][0]
ist der erste Zeiger auf ein int im Arrayx[0][0][0][0][0]
ist das erste int, auf das der Zeiger auf ein int zeigtIm letzten Beispiel ist die Verwendung semantisch viel klarer
*(*x)[0][0][0]
alsx[0][0][0][0][0]
, da der erste und der letzte[0]
hier aufgrund des Typs eher als Zeiger-Dereferenzierung als als Index in ein mehrdimensionales Array interpretiert werden. Sie sind jedoch(*x) == x[0]
unabhängig von der Semantik identisch . Sie können auch verwenden*****x
, was so aussieht, als würden Sie den Zeiger fünfmal dereferenzieren, aber er wird tatsächlich genauso interpretiert: ein Offset, eine Dereferenzierung, eine Dereferenzierung, zwei Offsets in ein Array und eine Dereferenzierung, allein aufgrund des Typs Sie wenden die Operation auf an.Wenn Sie
[0]
oder*
ein*
Nicht-Array-Typ verwendet werden, handelt es sich im Wesentlichen um einen Offset und eine Dereferenzierung aufgrund der Rangfolge von*(a + 0)
.Wenn Sie
[0]
oder*
ein*
auf einen Array - Typen dann ist es ein Offset dann einen idempotent dereferenzieren (das dereferenzieren wird vom Compiler aufgelöst die gleiche Adresse zu erhalten - es ist ein idempotent Betrieb).Wenn Sie
[0]
oder*
ein Typ mit einem 1d-Array-Typ, dann ist es ein Offset, dann eine DereferenzierungWenn Sie
[0]
oder**
ein 2d-Array-Typ, dann ist es nur ein Offset, dh ein Offset und dann eine idempotente Dereferenzierung.Wenn Sie
[0][0][0]
oder***
ein 3D-Array-Typ, dann ist es eine Offset + idempotente Dereferenzierung, dann eine Offset + idempotente Dereferenzierung, dann eine Offset + idempotente Dereferenzierung, dann eine Dereferenzierung. Die wahre Dereferenzierung tritt nur auf, wenn der Array-Typ vollständig entfernt ist.Zum Beispiel wird
int* (*x)[1][2][3]
der Typ der Reihe nach ausgepackt.x
hat einen Typint* (*)[1][2][3]
*x
hat einen Typint* [1][2][3]
(Offset 0 + idempotente Dereferenzierung)**x
hat einen Typint* [2][3]
(Offset 0 + idempotente Dereferenzierung)***x
hat einen Typint* [3]
(Offset 0 + idempotente Dereferenzierung)****x
hat einen Typint*
(Offset 0 + Dereferenzierung)*****x
hat Typint
(Offset 0 + Dereferenzierung)quelle