2016-08-06 37 views
9

Der Derefence-Operator * gibt, soweit ich weiß, den in der Pointer-Adresse gespeicherten Wert zurück. Was mich verwirrt, ist das Verhalten, wenn der Operator mit dem Zeiger eines Arrays verwendet wird. Zum BeispielVerwechslung des Dereferenzoperators ("*") in C

int a[4][2]; 

a Dann wird intern auf Zeiger des ersten Elements der Anordnung von 4 Elementen von 2 ints umgewandelt. Welchen Wert gibt *a zurück? Ich bin wirklich verwirrt!

+0

Es gibt keinen "Zeiger auf Array". Ein Array ist kein Zeiger. "Dann wird' a' intern zum Zeiger des ersten Elements der Anordnung von 4 Elementen von 2 Ints umgewandelt. " ist einfach falsch! – Olaf

+0

@Olaf Was meinst du mit gibt es keinen Zeiger auf Array? Bedeutet das nicht (int (* x) [4] [2]), dass x ein Zeiger auf ein Array von 4 Elementen von 2 Ints ist? – Jin

+0

Wo erscheint das in deiner Frage ?? Wenn Sie den Kommentar bis zum Ende lesen und die Antworten verstanden haben, sollte es klar sein. 'a' ist ein Array, kein Zeiger! Und es ist nicht per se "in einen Zeiger umgewandelt". Warum ist es für Anfänger so kompliziert zu verstehen, dass unterschiedliche Syntax unterschiedliche Typen erzeugt? – Olaf

Antwort

5

:

int a[4][2]; 

definiert a ein Array von 4 Elementen, von denen jeder ein Array von 2 int Elemente. (Ein 2-dimensionales Array ist nicht mehr und nicht weniger als ein Array von Arrays.)

Ein Array-Ausdruck wird in den meisten Kontexten implizit in einen Zeiger auf das ursprüngliche (nullte) Element des Array-Objekts konvertiert. (Man beachte die Annahme, dass es ein Array Objekt, dass einige Angst verursacht hat, aber es ist hier nicht relevant.)

Die Fälle, in denen ein Array Ausdruck nicht in einen Zeiger umgewandelt werden:

  • Wenn es der Operand sizeof ist;
  • Wenn es der Operand von unary & ist; und
  • Wenn es ein Zeichenfolgenliteral in einem Initialisierer ist, der zum Initialisieren eines Array-Objekts verwendet wird.

(Compiler-spezifische Erweiterungen wie gcc typeof könnten mehr Ausnahmen erstellen.)

So im Ausdruck *a, die subexpression a (die int[4][2] vom Typ ist) auf einen Zeiger vom Typ implizit int(*)[2] umgewandelt wird (Zeiger auf das Array von 2 int s).Die Anwendung von unary * dereferenziert diesen Zeiger und gibt uns einen Ausdruck vom Typ int[2].

Aber wir sind nicht ganz noch nicht getan. *a ist auch ein Ausdruck von Array-Typ, was bedeutet, dass, je nachdem, wie es verwendet wird, wahrscheinlich wieder in einen Zeiger umgewandelt werden, diesmal von Typ int*.

Wenn wir sizeof *a schreiben, die subexpression aint[4][2]-int(*)[2] umgewandelt wird, aber die subexpression *a ist nicht von int[2] zu int* umgewandelt, so dass der Ausdruck der Größe des Typs int[2] ergibt.

Wenn wir **a schreiben, tritt die Konvertierung auf. *a ist vom Typ int[2], der in int* umgewandelt wird; Dereferenzierung, die einen Ausdruck vom Typ int ergibt.

Beachten Sie, dass trotz der Tatsache, dass wir auf **a mit zwei Zeigerdereferenzierungsoperationen verweisen können, gibt es keinen Zeiger Objekte. a ist ein Array-Objekt, das vollständig aus 8 int Objekten besteht. Die impliziten Konvertierungen ergeben Zeiger Werte.

Die impliziten Array-to-Zeigerumwandlungsregeln sind in Abschnitt 6.3.2.1 N1570 Absatz 3 (Dieser Absatz gibt falsch _Alignof als vierte Ausnahme, aber _Alignof kann nicht auf einen Ausdruck angewendet werden. Die veröffentlichte C11 Standard korrigiert den Fehler .)

Empfohlener Messwert: Abschnitt 6 der comp.lang.c FAQ.

+1

Im Satz "Wenn wir sizeof * a schreiben, erfolgt die Konvertierung nicht" bedeutet die Umwandlung von Array von 2 ints zum Zeiger zum Anfangselement des Arrays von 2 ints? Ich denke auch, der nächste Absatz sollte wie folgt bearbeitet werden: "* a ist Typ int [2], der in int konvertiert wird" – Jin

+1

'* a 'hat nicht den Typ' int (*) [2]'; Es hat den Typ "int [2]". – 2501

+0

@ 2501: Ganz richtig. Ich habe die Antwort aktualisiert. –

9

Die Art der aint[4][2] ist, so dass die Art von *a (oder äquivalent a[0]) ist int[2].

Es ist nicht das gleiche wie a[0][0]. Wenn Sie dies tun:

int a[4][2]; 
printf("%d\n",*a); 

Der Compiler wird Ihnen sagen:

Warnung: Format '% d' erwartet Typ 'int', aber Argument 2 hat Typ 'int *'

Da ein Array (in diesem Fall eines vom Typ int [2]) an eine Funktion übergeben wird, wird in diesem Kontext ein Zeiger auf das erste Element zurückgegeben.

Wenn Sie andererseits **a hatten, entspricht das a[0][0] und hat den Typ int. Diese

+0

Wenn Sie von der Fehlermeldung "int *" verwirrt sind, werden die Ausdrücke des Array-Typs in einem anderen Kontext als '&' oder 'sizeof' oder verwandte Typen implizit in Zeiger konvertiert. So wird 'int [2]' zu 'int *' –

+0

@ChrisDodd: Was "verwandte Art von Abfragen" meinst du? Ein Array-Ausdruck wird in einen Zeiger umgewandelt, es sei denn, es ist (a) der Operand von unary '&', (b) der Operand von 'sizeof' oder (c) ein String-Literal in einem Initialisierer zur Initialisierung eines Arrays (sub) Objekt. Das sind die einzigen Ausnahmen. (Der N1570-Entwurf von C11 listet falsch einen vierten auf, aber der '_Alignof'-Operator kann nicht auf einen Ausdruck angewendet werden.) –

+0

@KeithThompson:' declltype', 'typeof' und' alignof', die in verschiedenen Erweiterungen existieren, sowie '_Generic'. Auch möglicherweise 'offsetof', obwohl in diesem Fall die Umwandlung in einen Zeiger keinen Effekt hätte. –