ChatGPT解决这个技术问题 Extra ChatGPT

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

I suspect ( sizeof( my_arr ) )[ 0 ] fails.
A recent duplicate has another variation on this syntax surprise: Why does sizeof(x)++ compile?

m
melpomene

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.)


I definitely knew that sizeof was a unary operator and not a function, but totally forgot. Woops. Thanks for the detailed explanation. The fact that [] is higher precedence than * is interesting regardless.
@melpomene Interesting. I've never thought of sizeof being a unary operator.
Don't you mean "... parses as sizeof (my_arr[0])"? Just adding a space doesn't really change anything.
I would recommend sizeof((my_array)[0]) instead
A
AnT stands with Russia

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].


The first version exists so that you can check the size of an integer in general on the machine (from back when there weren't even 64-bit machines!), but int isn't a valid expression, so you can't use the second form with it.
m
mch

[] 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.


Q
Quentin

You're using the version of the sizeof operator that takes an expression as parameter. Unlike the one that takes a type, it doesn't require parentheses. Hence, the operand is simply (my_arr)[0], with the parentheses being redundant.