web-dev-qa-db-fra.com

Pourquoi sizeof (my_arr) [0] est-il compilé et sizeof égal (my_arr [0])?

Pourquoi ce code est-il compilé?

_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 m'attendais à ce que la dernière ligne échoue, car je crois comprendre que sizeof() doit être évalué à un entier littéral, 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:

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

Fait intéressant, les éléments suivants ne parviennent pas à compiler (ce qui devrait faire la même chose, non?):

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

erreur: argument de type non valide '*' (ont 'un long entier non signé') _Static_assert (* sizeof (my_arr) == 4, "");

Si c'est important, j'utilise gcc 5.3.0

126
bgomberg

sizeof n'est pas une fonction. C'est un opérateur unaire comme ! Ou ~.

sizeof(my_arr)[0] analyse en tant que sizeof (my_arr)[0], qui est simplement sizeof my_arr[0] avec des parenthèses redondantes.

C'est comme si !(my_arr)[0] analysait comme !(my_arr[0]).

En général, les opérateurs postfixés ont une priorité plus élevée que les opérateurs préfixés en C. sizeof *a[i]++ Est analysé sous la forme sizeof (*((a[i])++)) (les opérateurs postfixés [] Et ++ Sont appliqués à a d'abord, puis les opérateurs de préfixe * et sizeof).

(Ceci est la version de l'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 requises et feront partie de sizeof syntaxe.)

192
melpomene

sizeof a deux "versions": sizeof(type name) et sizeof expression. Le premier nécessite une paire de () Autour de son argument. Mais le 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 de l'argument et non de la syntaxe sizeof.

Puisque my_arr Est connu du compilateur comme étant un nom d'objet et non un nom de type, votre sizeof(my_arr)[0] est en fait vu par le compilateur comme sizeof appliqué à une expression: sizeof (my_arr)[0], où (my_arr)[0] est l'expression de l'argument. Le () Entourant le nom du tableau est purement superflu. L'expression entière est interprétée comme sizeof my_arr[0]. Ceci est équivalent à votre précédente sizeof(my_arr[0]).

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

C'est une idée fausse assez répandue que la syntaxe de sizeof nécessite en quelque sorte une paire de () Autour de son argument. Cette idée fausse est ce qui induit en erreur l'intuition des gens lorsqu'ils interprètent de telles expressions comme sizeof(my_arr)[0].

45
AnT

[] A une plus haute priorité que sizeof. Donc, sizeof(my_arr)[0] est identique à sizeof((my_arr)[0]).

Ici est un lien vers un tableau de priorité.

24
mch

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

7
Quentin