Warum wird sizeof (my_arr) [0] kompiliert und gleich sizeof (my_arr [0])?

129

Warum wird dieser Code kompiliert?

_Static uint32_t my_arr[2];
_Static_assert(sizeof(my_arr) == 8, "");
_Static_assert(sizeof(my_arr[0]) == 4, "");
_Static_assert(sizeof(my_arr)[0] == 4, "");

Die ersten beiden Behauptungen sind offensichtlich korrekt, aber ich hätte erwartet, dass die letzte Zeile fehlschlägt, da ich verstehe, dass dies sizeof()zu einem ganzzahligen Literal ausgewertet werden sollte, das nicht als Array behandelt werden kann. Mit anderen Worten, es würde genauso fehlschlagen wie die folgende Zeile:

_Static_assert(4[0] == 4, "");

Interessanterweise kann das Folgende tatsächlich nicht kompiliert werden (was sollte dasselbe tun, nein?):

_Static_assert(*sizeof(my_arr) == 4, "");

Fehler: ungültiges Typargument von unary '*' (habe 'long unsigned int') _Static_assert (* sizeof (my_arr) == 4, "");

Wenn es darauf ankommt, verwende ich gcc 5.3.0

bgomberg
quelle
15
Ich vermute ( sizeof( my_arr ) )[ 0 ]scheitert.
Andrew Henle
Ein aktuelles Duplikat enthält eine weitere Variation dieser Syntaxüberraschung: Warum wird sizeof (x) ++ kompiliert?
Peter Cordes

Antworten:

197

sizeofist keine Funktion. Es ist ein unärer Operator wie !oder ~.

sizeof(my_arr)[0]analysiert als sizeof (my_arr)[0], was nur sizeof my_arr[0]mit redundanten Klammern ist.

Dies ist genau wie das !(my_arr)[0]Parsen als !(my_arr[0]).

Im Allgemeinen haben Postfix-Operatoren eine höhere Priorität als Präfix-Operatoren in C. sizeof *a[i]++parses as sizeof (*((a[i])++))(die Postfix-Operatoren []und ++werden zuerst angewendet a, dann die Präfix-Operatoren *und sizeof).

(Dies ist die Ausdrucksversion von sizeof. Es gibt auch eine Typversion, die einen Typnamen in Klammern verwendet: sizeof (TYPE)In diesem Fall wären die Parens erforderlich und Teil der sizeofSyntax.)

Melpomene
quelle
14
Ich wusste definitiv, dass sizeof ein unärer Operator und keine Funktion ist, aber völlig vergessen. Woops. Danke für die ausführliche Erklärung. Die Tatsache, dass [] eine höhere Priorität als * hat, ist trotzdem interessant.
Bgomberg
@melpomene Interessant. Ich habe nie daran gedacht sizeof, ein unärer Operator zu sein.
Mutantkeyboard
5
Meinst du nicht "... parses as sizeof (my_arr[0])"? Das Hinzufügen eines Leerzeichens ändert nichts.
Bernhard Barker
Ich würde sizeof((my_array)[0])stattdessen empfehlen
Bolov
46

sizeofhat zwei "Versionen": sizeof(type name)und sizeof expression. Ersteres erfordert ein Paar ()um seine Argumentation. Aber der letztere - derjenige mit einem Ausdruck als Argument - hat kein ()Argument. Was auch immer ()Sie im Argument verwenden, wird als Teil des Argumentausdrucks und nicht als Teil der sizeofSyntax selbst angesehen.

Da my_arrder Compiler als Objektname und nicht als Typname bekannt ist, wird Ihr sizeof(my_arr)[0]Compiler vom Compiler tatsächlich als sizeofauf einen Ausdruck angewendet angesehen : sizeof (my_arr)[0], wobei (my_arr)[0]der Argumentausdruck ist. Die ()Umgebung des Array-Namens ist rein überflüssig. Der gesamte Ausdruck wird interpretiert als sizeof my_arr[0]. Dies entspricht Ihrem vorherigen sizeof(my_arr[0]).

(Dies bedeutet übrigens, dass Ihr Vorgänger sizeof(my_arr[0])auch ein Paar überflüssiger enthält ().)

Es ist ein weit verbreitetes Missverständnis, dass sizeofdie Syntax irgendwie ein Paar von ()Argumenten erfordert . Dieses Missverständnis führt die Intuition der Menschen in die Irre, wenn sie solche Ausdrücke wie interpretiert sizeof(my_arr)[0].

Ameise
quelle
1
Die erste Version ist vorhanden, damit Sie die Größe einer Ganzzahl im Allgemeinen auf dem Computer überprüfen können (von damals, als es noch nicht einmal 64-Bit-Computer gab!), Ist intjedoch kein gültiger Ausdruck, sodass Sie den nicht verwenden können zweite Form damit.
NH.
25

[]haben eine höhere Priorität als sizeof. So sizeof(my_arr)[0]ist das gleiche wie sizeof((my_arr)[0]).

Hier ist ein Link zu einer Prioritätstabelle.

mch
quelle
8

Sie verwenden die Version des sizeofOperators, der einen Ausdruck als Parameter verwendet. Anders als bei einem Typ sind keine Klammern erforderlich. Daher ist der Operand einfach (my_arr)[0], wobei die Klammern redundant sind.

QUentin
quelle