Warum ist x [0]! = X [0] [0]! = X [0] [0] [0]?

149

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 *xist 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]?

Leo91
quelle
58
x[0], x[0][0]Und x[0][0][0]verschiedene Typen haben. Sie können nicht verglichen werden. Was meinst du damit !=?
Bolov
4
@ Celticminstrel sie sind nicht gleich: int **x[5]ist ein Array von 5 Elementen. Ein Element ist ein Zeiger auf Zeiger auf int`
bolov
5
@celticminstrel 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.
Emlai
5
@ Celticminstrel Rechts-Links-Regel , Spirale-Regel , C-Kauderwelsch ↔ Englisch und Ihr 'auf dem Weg zum Drei-Sterne-Programmierer :)
Bolov
5
@ Leo91: Erstens hast du hier zwei Zeigerebenen , nicht drei. Zweitens, was bedeutet x[0] != x[0][0] != x[0][0][0]das? Dies ist kein gültiger Vergleich in C ++. Auch wenn Sie es in aufgeteilt haben x[0] != x[0][0]und x[0][0] != x[0][0][0]es immer noch nicht gültig ist. Was bedeutet Ihre Frage?
Am

Antworten:

261

xist ein Zeiger auf ein Array von 5 Zeigern auf int.
x[0]ist ein Array von 5 Zeigern auf int.
x[0][0]ist ein Zeiger auf ein int.
x[0][0][0]ist ein int.

                       x[0]
   Pointer to array  +------+                                 x[0][0][0]         
x -----------------> |      |         Pointer to int           +-------+
               0x500 | 0x100| x[0][0]---------------->   0x100 |  10   |
x is a pointer to    |      |                                  +-------+
an array of 5        +------+                        
pointers to int      |      |         Pointer to int                             
               0x504 | 0x222| x[0][1]---------------->   0x222                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x508 | 0x001| x[0][2]---------------->   0x001                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x50C | 0x123| x[0][3]---------------->   0x123                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x510 | 0x000| x[0][4]---------------->   0x000                    
                     |      |                                             
                     +------+                                             

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. Daher x[0]wird die Adresse seines ersten Elements angegeben, x[0][0]das ist 0x500.
  • x[0][0]enthält die Adresse eines intwas ist 0x100.
  • x[0][0][0]enthält einen intWert von 10.

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].

Haccks
quelle
Dieses Diagramm ist für mich etwas verwirrend: Es 0x100sollte sofort links neben der Box erscheinen 10, genau wie 0x500links neben der Box. Anstatt weit links und unten zu sein.
MM
@MattMcNabb; Ich denke nicht, dass es verwirrend sein sollte, sondern Änderungen gemäß Ihrem Vorschlag für mehr Klarheit.
Haccks
4
@haccks - Es ist mir ein Vergnügen :) Der Grund, warum dieses Diagramm großartig ist, ist, dass Sie nicht einmal die Erklärung brauchen, die Sie gegeben haben, die darauf folgt. Dieses Diagramm selbst ist selbsterklärend, da es die Frage bereits beantwortet. Der folgende Text ist einfach ein Bonus.
Rayryeng
1
Sie können auch yed verwenden, eine Diagrammsoftware. Es hilft mir sehr, meine Gedanken zu organisieren
rpax
@GrijeshChauhan Ich benutze asciiflow für die Kommentare des Codes, yeD für Präsentationen :)
rpax
133
x[0] != x[0][0] != x[0][0][0]

ist nach Ihrem eigenen Beitrag,

*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`  

das ist vereinfacht

*x != **x != ***x

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 intWert.

deviantfan
quelle
Ich kann nicht verstehen ... ob x [0], x [0] [0], x [0] [0] [0] äquivalent zu * (x + 0), * (x + 0 + 0) ist , * (x + 0 + 0 + 0), warum sollten sie unterschiedliche Adressen haben?
Leo91
41
@ Leo91 x[0][0]ist (x[0])[0]also *((*(x+0))+0)nicht *(x+0+0). Dereferenzierung erfolgt vor der Sekunde [0].
Emlai
4
@ Leo91 x[0][0] != *(x+0+0)genau wie x[2][3] != x[3][2].
OZG
@ Leo91 Der zweite Kommentar, den du "jetzt bekommen" hast, wurde entfernt. Verstehst du etwas nicht (was in der Antwort besser erklärt werden könnte) oder tust du das nicht? (Einige Leute löschen gerne Kommentare ohne viel informativen Inhalt)
Deviantfan
@deviantfan sorry ich kann nicht verstehen was du meinst. Ich verstehe die Antworten sowie viele Kommentare, die dazu beigetragen haben, das Konzept zu klären.
Leo91
50

Hier ist das Speicherlayout Ihres Zeigers:

   +------------------+
x: | address of array |
   +------------------+
            |
            V
            +-----------+-----------+-----------+-----------+-----------+
            | pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
            +-----------+-----------+-----------+-----------+-----------+
                  |
                  V
                  +--------------+
                  | some integer |
                  +--------------+

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:

  1. Sie können x[0]an den sizeofOperator übergeben. Aber das ist nicht wirklich eine Verwendung des Wertes, sein Ergebnis hängt nur vom Typ ab.

  2. Sie können die Adresse nehmen, die den Wert von ergibt x, dh "Adresse des Arrays" mit dem Typ int*(*)[5]. Mit anderen Worten:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x

  3. In allen anderen Kontextenx[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 Typ int**. Der Effekt ist der gleiche, als hätten Sie xauf einen Zeiger vom Typ gewirkt int**.

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 Aufruf printf("%p", x[0])druckt den Inhalt der Speicherzellen, die als "Adresse des Arrays" gekennzeichnet sind.

cmaster - Monica wieder einsetzen
quelle
1
x[0]ist nicht die Adresse des Arrays.
Haccks
1
@haccks Ja, nach dem Buchstaben des Standards 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.
cmaster - wieder herstellen Monica
tolle Grafiken, die es perfekt erklären!
MK
"Arrays sind jedoch etwas, mit dem man in C eigentlich nichts anfangen kann." -> Zählerbeispiel: printf("%zu\n", sizeof x[0]);Gibt die Größe des Arrays an, nicht die Größe eines Zeigers.
chux - Monica
@ chux-ReinstateMonica Und ich fuhr fort: "Sie manipulieren immer entweder ihre Adresse oder ihre Elemente, niemals das gesamte Array als Ganzes", gefolgt von Punkt 1 der Aufzählung, in dem ich über die Wirkung von sizeof x[0]...
cmaster - spreche. wieder einzusetzen monica
18
  • 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 auf int;
  • x[0][0]dereferenziert den äußersten Zeiger und indiziert das Array, was zu einem Zeiger auf führt int;
  • 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 .

d125q
quelle
11

Lassen Sie Schritt für Schritt Ausdrücke betrachten x[0], x[0][0]und x[0][0][0].

Wie xfolgt definiert

int *(*x)[5];

dann ist expression x[0]ein Array vom Typ int *[5]. Berücksichtigen Sie, dass Ausdruck x[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ärung

int * y[5];

Ausdruck x[0][0]ist äquivalent zu y[0]und hat Typ int *. Bezeichne es wie z, das heißt, wir haben eine Deklaration

int *z;

Ausdruck x[0][0][0]ist äquivalent zu Ausdruck y[0][0], der wiederum äquivalent zu Ausdruck ist z[0]und Typ hat int.

Also haben wir

x[0] hat Typ int *[5]

x[0][0] hat Typ int *

x[0][0][0] hat Typ int

Sie sind also Objekte unterschiedlichen Typs und unterschiedlicher Größe.

Zum Beispiel ausführen

std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;
Vlad aus Moskau
quelle
10

Als erstes muss ich das sagen

x [0] = * (x + 0) = * x;

x [0] [0] = * (* (x + 0) + 0) = * * x;

x [0] [0] [0] = * (* (* (x + 0) + 0)) = * * * x;

Also * x ≠ * * x ≠ * * * x

Aus dem folgenden Bild sind alle Dinge klar.

  x[0][0][0]= 2000

  x[0][0]   = 1001

  x[0]      = 10

Geben Sie hier die Bildbeschreibung ein

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:

{
int ***x;
x=(int***)malloc(sizeof(int***));
*x=(int**)malloc(sizeof(int**));
**x=(int*)malloc(sizeof(int*));
***x=10;
printf("%d   %d   %d   %d\n",x,*x,**x,***x);
printf("%d   %d   %d   %d   %d",x[0][0][0],x[0][0],x[0],x,&x);
}

Ausgabe

142041096 142041112 142041128 10
10 142041128 142041112 142041096 -1076392836

Programm 2:

{
int x[1][1][1]={10};
printf("%d   %d   %d   %d \n ",x[0][0][0],x[0][0],x[0],&x);
}

Ausgabe

10   -1074058436   -1074058436   -1074058436 
apm
quelle
3
Ihre Antwort ist irreführend. x[0]enthält keine Ameisenadresse. Es ist ein Array. Es verfällt, um auf sein erstes Element zu zeigen.
Haccks
Ähm ... was bedeutet das? Ihre Bearbeitung ist wie Kirsche auf Kuchen auf Ihre falsche Antwort. Es macht keinen Sinn
haccks
@haccks Wenn nur Zeiger verwendet werden, ist diese Antwort richtig. Es wird einige Änderungen im Adressbereich geben, wenn Array
apm
7

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

David Optionaler Courtenay
quelle
1
x[0][0]Wäre es nicht eine einzige Kiste voller Papierstücke, auf denen die Positionen der Schuhkartons stehen?
Wchargin
4

In C ++ gibt es ein Prinzip: Eine Deklaration einer Variablen gibt genau an, wie die Variable verwendet wird. Betrachten Sie Ihre Erklärung:

int *(*x)[5];

das kann umgeschrieben werden als (zur Verdeutlichung):

int *((*x)[5]);

Aufgrund des Prinzips haben wir:

*((*x)[i]) is treated as an int value (i = 0..4)
 (*x)[i] is treated as an int* pointer (i = 0..4)
 *x is treated as an int** pointer
 x is treated as an int*** pointer

Deshalb:

x[0] is an int** pointer
 x[0][0] = (x[0]) [0] is an int* pointer
 x[0][0][0] = (x[0][0]) [0] is an int value

So können Sie den Unterschied herausfinden.

Nghia Bui
quelle
1
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).
MM
Okay, aber Sie sollten sagen: x [0] ist ein Array von 5 Zeigern int *
Nghia Bui
Um die richtige Ableitung per @MattMcNabb zu geben: *(*x)[5]ist ein int, so (*x)[5]ist ein int *, so *xist ein (int *)[5], so xist ein *((int *)[5]). Das heißt, xist ein Zeiger auf ein 5-Array von Zeigern auf int.
Wchargin
2

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

 int y [5][5][5];

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 Adresse

int **x[5];

belegt keinen zusammenhängenden Raum.

xund x [0]haben die gleiche Adresse, aber x[0][0]und x[0][0][0]sind jeweils an unterschiedlichen Adressen

Glenn Teitelbaum
quelle
2
int *(*x)[5]ist anders alsint **x[5]
MM
2

Ein pZeiger sein: Sie stapeln Dereferenzen mit p[0][0], was äquivalent zu ist *((*(p+0))+0).

In C-Referenz (&) und Dereferenzierungsnotation (*):

p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])

Ist äquivalent zu:

p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0

Schauen Sie, das & * kann überarbeitet werden, indem Sie es einfach entfernen:

p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)
Luciano
quelle
Was versuchst du nach deinem ersten Satz mit allem zu zeigen? Sie haben nur viele Variationen p == p . &(&p[0])[0]ist anders alsp[0][0]
MM
Der Typ fragte, warum 'x [0]! = X [0] [0]! = X [0] [0] [0]', wenn x ein Zeiger ist, richtig? Ich habe versucht ihm zu zeigen, dass er durch die C-Notation der Dereferenzierung (*) gefangen werden kann, wenn er [0] stapelt. Es ist also ein Versuch, ihm die richtige Notation zu zeigen, damit x gleich x [0] ist, wobei x [0] erneut mit & referenziert wird, und so weiter.
Luciano
1

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 xauf 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 .

#include <stdio.h>

int main () {
  int *(*x)[5];

  x = (int *(*)[5]) &x;

  printf("%p\n", x[0]);
  printf("%p\n", x[0][0]);
  printf("%p\n", x[0][0][0]);
}

Dies wird sowohl in C89 als auch in C99 ohne Warnungen kompiliert, und die Ausgabe lautet wie folgt:

$ ./ptrs
0xbfd9198c
0xbfd9198c
0xbfd9198c

Interessanterweise sind alle drei Werte identisch. Das sollte aber keine Überraschung sein! Lassen Sie uns zunächst das Programm aufschlüsseln.

Wir deklarieren xals 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), xbezieht sich also auf einen tatsächlichen Speicherort. In der C-Sprachfamilie ist der Inhalt von xnur Müll, etwas, das von der vorherigen Nutzung des Standorts übrig geblieben ist, sodass er xselbst nirgendwo hinweist - schon gar nicht auf zugewiesenen Speicherplatz.

Natürlich können wir die Adresse der Variablen nehmen xund irgendwo ablegen , also tun wir genau das. Aber wir werden weitermachen und es in x selbst setzen. Da es &xsich um einen anderen Typ handelt x, müssen wir eine Besetzung durchführen, damit wir keine Warnungen erhalten.

Das Speichermodell würde ungefähr so ​​aussehen:

0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+

Der 4-Byte-Speicherblock an der Adresse 0xbfd9198centhält also das Bitmuster, das dem Hexadezimalwert entspricht 0xbfd9198c. 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 mallocerstellen Sie das Array mit 5 int-Zeigern und erneut die Ints, auf die in diesem Array verwiesen wird. mallocGibt 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]und x[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!

Purag
quelle
Ich würde sagen, dass eine andere seltsame Verwendung von Zeigern ich jemals gesehen habe.
Haccks
@haccks Ja, es ist ziemlich seltsam, aber wenn man es aufschlüsselt, ist es genauso einfach wie die anderen Beispiele. Es ist einfach ein Fall, in dem die Bitmuster alle gleich sind.
Purag
Ihr Code verursacht undefiniertes Verhalten. x[0]stellt eigentlich kein gültiges Objekt des richtigen Typs dar
MM
@MattMcNabb es ist undefiniert, und das ist mir eigentlich sehr klar. Ich bin nicht einverstanden mit dem Typ. xist 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 von x[0]ist ein Array, und C beschwert sich nicht, wenn Sie es mit drucken, %pda es ohnehin so darunter implementiert ist.
Purag
Und das Kompilieren mit dem -pedanticFlag erzeugt keine Warnungen, so dass C mit den Typen in
Ordnung ist
0

Der Typ von int *(*x)[5]ist int* (*)[5]dh ein Zeiger auf ein Array von 5 Zeigern auf Ints.

  • xist die Adresse des ersten Arrays von 5 Zeigern auf Ints (eine Adresse mit Typ int* (*)[5])
  • x[0]die Adresse des ersten Arrays von 5 Zeigern auf Ints (gleiche Adresse mit Typ int* [5]) (Versatzadresse x um 0*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 Typ int*) (Offset-Adresse x um 0*sizeof(int* [5])und Dereferenzierung und dann um 0*sizeof(int*)und Dereferenzierung)
  • x[0][0][0]ist das erste int, auf das der Zeiger auf ein int zeigt (Offset-Adresse x um 0*sizeof(int* [5])und Dereferenzierung und Offset dieser Adresse um 0*sizeof(int*)und Dereferenzierung und Offset dieser Adresse um 0*sizeof(int)und Dereferenzierung)

Der Typ von int *(*y)[5][5][5]ist int* (*)[5][5][5]dh ein Zeiger auf ein 3D-Array von 5x5x5 Zeigern auf Ints

  • x ist die Adresse des ersten 3D-Arrays von 5x5x5 Zeigern auf Ints mit Typ int*(*)[5][5][5]
  • x[0]ist die Adresse des ersten 3D-Arrays von 5x5x5 Zeigern auf Ints (Offset-Adresse x um 0*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 um 0*sizeof(int* [5][5][5])und Dereferenzierung, dann Offset dieser Adresse um 0*sizeof(int* [5][5]))
  • x[0][0][0]ist die Adresse des ersten Arrays von 5 Zeigern auf Ints (Versatzadresse x um 0*sizeof(int* [5][5][5])und Dereferenzierung und Versatz dieser Adresse um 0*sizeof(int* [5][5])und Versatz dieser Adresse um 0*sizeof(int* [5]))
  • x[0][0][0][0]ist der erste Zeiger auf ein int im Array (Versatzadresse x um 0*sizeof(int* [5][5][5])und Dereferenzierung und Versatz dieser Adresse um 0*sizeof(int* [5][5])und Versatz dieser Adresse um 0*sizeof(int* [5])und Versatz dieser Adresse um 0*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 um 0*sizeof(int* [5][5][5])und Dereferenzierung und Offset dieser Adresse um 0*sizeof(int* [5][5])und Offset dieser Adresse um 0*sizeof(int* [5])und Offset dieser Adresse um 0*sizeof(int*)und Dereferenzierung und Offset dieser Adresse um 0*sizeof(int)und Dereferenzierung)

Wie für Array-Zerfall:

void function (int* x[5][5][5]){
  printf("%p",&x[0][0][0][0]); //get the address of the first int pointed to by the 3d array
}

Dies ist gleichbedeutend mit dem Bestehen int* x[][5][5]oder int* (*x)[5][5]dh sie verfallen alle zu letzterem. Aus diesem Grund erhalten Sie keine Compiler-Warnung für die Verwendung x[6][0][0]in der Funktion, dies wird jedoch der Fall sein, x[0][6][0]da diese Größeninformationen erhalten bleiben

void function (int* (*x)[5][5][5]){
  printf("%p",&x[0][0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
  • x[0] ist die Adresse des ersten 3D-Arrays von 5x5x5 Zeigern auf Ints
  • x[0][0] ist die Adresse des ersten 2d-Arrays von 5x5 Zeigern auf Ints
  • x[0][0][0] ist die Adresse des ersten Arrays von 5 Zeigern auf Ints
  • x[0][0][0][0] ist der erste Zeiger auf ein int im Array
  • x[0][0][0][0][0] ist das erste int, auf das der Zeiger auf ein int zeigt

Im letzten Beispiel ist die Verwendung semantisch viel klarer *(*x)[0][0][0]als x[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 Dereferenzierung

Wenn 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 Typ int* (*)[1][2][3]
  • *xhat einen Typ int* [1][2][3](Offset 0 + idempotente Dereferenzierung)
  • **xhat einen Typ int* [2][3](Offset 0 + idempotente Dereferenzierung)
  • ***xhat einen Typ int* [3](Offset 0 + idempotente Dereferenzierung)
  • ****xhat einen Typ int*(Offset 0 + Dereferenzierung)
  • *****xhat Typ int(Offset 0 + Dereferenzierung)
Lewis Kelsey
quelle