Why does sizeof(my_arr)[0] compile and equal sizeof(my_arr[0])?

Why does this code compile?

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

The first 2 asserts are obviously correct, but I would have expected the last line to fail, as my understanding is that sizeof() should evaluate to an integer literal, which can't be treated as an array. In other words, it would fail in the same way that the following line fails:

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

Interestingly, the following does indeed fail to compile (which should be doing the same thing, no?):

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

error: invalid type argument of unary '*' (have 'long unsigned int') _Static_assert(*sizeof(my_arr) == 4, "");

If it matters, I'm using gcc 5.3.0


Solution 1:

sizeof is not a function. It's a unary operator like ! or ~.

sizeof(my_arr)[0] parses as sizeof (my_arr)[0], which is just sizeof my_arr[0] with redundant parentheses.

This is just like !(my_arr)[0] parses as !(my_arr[0]).

In general, postfix operators have higher precedence than prefix operators in C. sizeof *a[i]++ parses as sizeof (*((a[i])++)) (the postfix operators [] and ++ are applied to a first, then the prefix operators * and sizeof).

(This is the expression version of sizeof. There's also a type version, which takes a parenthesized type name: sizeof (TYPE). In that case the parens would be required and part of the sizeof syntax.)

Solution 2:

sizeof has two "versions": sizeof(type name) and sizeof expression. The former requires a pair of () around its argument. But the latter - the one with an expression as an argument - does not have () around its argument. Whatever () you use in the argument is seen as part of the argument expression, not part of sizeof syntax itself.

Since my_arr is known to the compiler as an object name, not a type name, your sizeof(my_arr)[0] is actually seen by the compiler as sizeof applied to an expression: sizeof (my_arr)[0], where (my_arr)[0] is the argument expression. The () surrounding the array name is purely superfluous. The whole expression is interpreted as sizeof my_arr[0]. This is equivalent to your previous sizeof(my_arr[0]).

(This means, BTW, that your previous sizeof(my_arr[0]) also contains a pair of superfluous ().)

It is a rather widespread misconception that sizeof's syntax somehow requires a pair of () around its argument. This misconception is what misleads people's intuition when interpreting such expressions as sizeof(my_arr)[0].

Solution 3:

[] have a higher precendence than sizeof. So sizeof(my_arr)[0] is the same as sizeof((my_arr)[0]).

Here is a link to a precedence table.