Почему sizeof (my_arr) [0] компилируется и равен sizeof (my_arr [0])?
Почему этот код компилируется?
_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, "");
Первые два утверждения, очевидно, правильны, но я ожидал, что последняя строка не сработает, поскольку я понимаю, что sizeof()
должен оценивать целочисленный литерал, который нельзя рассматривать как массив. Другими словами, он потерпел бы неудачу так же, как и следующая строка:
_Static_assert(4[0] == 4, "");
Интересно, что следующее действительно не удается скомпилировать (что должно делать то же самое, нет?):
_Static_assert(*sizeof(my_arr) == 4, "");
error: недопустимый аргумент типа унарного '*' (есть "long unsigned int" ) _Static_assert (* sizeof (my_arr) == 4, "");
Если это важно, я использую gcc 5.3.0
Ответы
Ответ 1
sizeof
не является функцией. Это унарный оператор, такой как !
или ~
.
sizeof(my_arr)[0]
анализирует как sizeof (my_arr)[0]
, который просто sizeof my_arr[0]
с резервными скобками.
Это так же, как !(my_arr)[0]
анализирует как !(my_arr[0])
.
В общем случае операторы постфикса имеют более высокий приоритет, чем префиксные операторы в C. sizeof *a[i]++
анализируются как sizeof (*((a[i])++))
(операторы postfix []
и ++
применяются сначала к a
, тогда префиксные операторы *
> и sizeof
).
(Это версия выражения sizeof
. Также существует версия типа, которая принимает имя типа в скобках: sizeof (TYPE)
. В этом случае требуется использовать parens и часть синтаксиса sizeof
.)
Ответ 2
sizeof
имеет две "версии": sizeof(type name)
и sizeof expression
. Первая требует пары ()
вокруг своего аргумента. Но последний - тот, у которого есть выражение как аргумент, не имеет ()
вокруг своего аргумента. Независимо от того, что ()
, которое вы используете в аргументе, рассматривается как часть выражения аргумента, а не как часть синтаксиса sizeof
.
Поскольку my_arr
известен компилятору как имя объекта, а не имя типа, ваш sizeof(my_arr)[0]
фактически рассматривается компилятором как sizeof
, примененным к выражению: sizeof (my_arr)[0]
, где (my_arr)[0]
выражение аргумента. ()
, окружающий имя массива, является излишним. Все выражение интерпретируется как sizeof my_arr[0]
. Это эквивалентно предыдущему sizeof(my_arr[0])
.
(Это значит, BTW, что предыдущий sizeof(my_arr[0])
также содержит пару лишних ()
.)
Это довольно распространенное заблуждение, что синтаксис sizeof
так или иначе требует пары ()
вокруг его аргумента. Это заблуждение - это то, что вводит в заблуждение людей интуицию при интерпретации таких выражений, как sizeof(my_arr)[0]
.
Ответ 3
[]
имеют более высокую точность, чем sizeof
. Итак, sizeof(my_arr)[0]
совпадает с sizeof((my_arr)[0])
.
Здесь - ссылка на таблицу приоритетов.
Ответ 4
Вы используете версию оператора sizeof
, которая принимает выражение как параметр. В отличие от того, который принимает тип, он не требует скобок. Следовательно, операнд просто (my_arr)[0]
, причем круглые скобки являются избыточными.