Pourquoi sizeof (my_arr) [0] compile-t-il et est-il égal à sizeof (my_arr [0])?

129

Pourquoi ce code se compile-t-il?

_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, "");

Les 2 premières assertions sont évidemment correctes, mais je me serais attendu à ce que la dernière ligne échoue, car je crois comprendre que cela sizeof()devrait être évalué à un littéral entier, qui ne peut pas être traité comme un tableau. En d'autres termes, il échouerait de la même manière que la ligne suivante échoue:

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

Fait intéressant, ce qui suit échoue en effet à compiler (ce qui devrait faire la même chose, non?):

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

erreur: argument de type non valide d'unaire '*' (avoir 'long unsigned int') _Static_assert (* sizeof (my_arr) == 4, "");

Si cela compte, j'utilise gcc 5.3.0

bgomberg
la source
15
Je soupçonne l' ( sizeof( my_arr ) )[ 0 ]échec.
Andrew Henle
Un doublon récent a une autre variation de cette syntaxe surprenante: pourquoi sizeof (x) ++ compile-t-il?
Peter Cordes

Réponses:

197

sizeofn'est pas une fonction. C'est un opérateur unaire comme !ou ~.

sizeof(my_arr)[0]analyse comme sizeof (my_arr)[0], qui est juste sizeof my_arr[0]avec des parenthèses redondantes.

C'est exactement comme l' !(my_arr)[0]analyse !(my_arr[0]).

En général, les opérateurs de suffixe ont une priorité plus élevée que les opérateurs de préfixe dans C. sizeof *a[i]++analyse comme sizeof (*((a[i])++))(les opérateurs de suffixe []et ++sont appliqués en apremier, puis les opérateurs de préfixe *et sizeof).

(Il s'agit de la version d'expression de sizeof. Il existe également une version de type, qui prend un nom de type entre parenthèses:. sizeof (TYPE)Dans ce cas, les parenthèses seraient obligatoires et feraient partie de la sizeofsyntaxe.)

melpomène
la source
14
Je savais vraiment que sizeof était un opérateur unaire et non une fonction, mais je l'ai totalement oublié. Woops. Merci pour l'explication détaillée. Le fait que [] soit une priorité plus élevée que * est intéressant malgré tout.
bgomberg
@melpomene Intéressant. Je n'ai jamais pensé sizeofêtre un opérateur unaire.
mutantkeyboard
5
Tu ne veux pas dire "... parses as sizeof (my_arr[0])"? L'ajout d'un espace ne change vraiment rien.
Bernhard Barker
Je recommanderais sizeof((my_array)[0])plutôt
bolov
46

sizeofa deux "versions": sizeof(type name)et sizeof expression. Le premier nécessite une paire de ()autour de son argument. Mais ce dernier - celui avec une expression comme argument - n'a pas ()autour de son argument. Tout ()ce que vous utilisez dans l'argument est considéré comme faisant partie de l'expression d'argument et non comme faisant partie de la sizeofsyntaxe elle-même.

Étant donné que my_arrle compilateur est connu comme un nom d'objet et non comme un nom de type, votre sizeof(my_arr)[0]est en fait vu par le compilateur comme sizeofappliqué à une expression:, sizeof (my_arr)[0](my_arr)[0]est l'expression d'argument. Le ()nom du tableau qui entoure est purement superflu. L'expression entière est interprétée comme sizeof my_arr[0]. Ceci est équivalent à votre précédent sizeof(my_arr[0]).

(Cela signifie, BTW, que votre précédent sizeof(my_arr[0])contient également une paire de superflus ().)

C'est une idée fausse assez répandue selon laquelle sizeofla syntaxe de la syntaxe nécessite en quelque sorte une paire de ()autour de son argument. Cette idée fausse est ce qui trompe l'intuition des gens lors de l'interprétation de telles expressions comme sizeof(my_arr)[0].

Fourmi
la source
1
La première version existe pour que vous puissiez vérifier la taille d'un entier en général sur la machine (à partir de l'époque où il n'y avait même pas de machines 64 bits!), Mais intn'est pas une expression valide, vous ne pouvez donc pas utiliser le deuxième forme avec elle.
NH.
25

[]ont une priorité plus élevée que sizeof. C'est donc sizeof(my_arr)[0]la même chose que sizeof((my_arr)[0]).

Voici un lien vers une table de priorité.

mch
la source
8

Vous utilisez la version de l' sizeofopérateur qui prend une expression comme paramètre. Contrairement à celui qui prend un type, il ne nécessite pas de parenthèses. Par conséquent, l'opérande est simplement (my_arr)[0], les parenthèses étant redondantes.

Quentin
la source