Почему sizeof (my_arr) [0] компилируется и равен sizeof (my_arr [0])?

129

Почему этот код компилируется?

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

Первые 2 утверждения, очевидно, верны, но я ожидал, что последняя строка не удастся, поскольку я понимаю, что она sizeof()должна оцениваться как целочисленный литерал, который нельзя рассматривать как массив. Другими словами, произойдет сбой так же, как и в следующей строке:

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

Интересно, что следующее действительно не компилируется (что должно делать то же самое, не так ли?):

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

ошибка: недопустимый аргумент типа унарного '*' (есть 'long unsigned int') _Static_assert (* sizeof (my_arr) == 4, "");

Если это важно, я использую gcc 5.3.0

bgomberg
источник
15
Я подозреваю, что это ( sizeof( my_arr ) )[ 0 ]не так.
Эндрю Хенле
У недавнего дубликата есть еще одна вариация этого синтаксического сюрприза: почему компилируется sizeof (x) ++?
Питер Кордес

Ответы:

197

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])++))( сначала применяются постфиксные операторы []и , затем префиксные операторы и ).++a*sizeof

(Это версия выражения sizeof. Существует также версия типа, которая принимает имя типа в скобках:. sizeof (TYPE)В этом случае скобки потребуются и являются частью sizeofсинтаксиса.)

Мельпомена
источник
14
Я точно знал, что sizeof был унарным оператором, а не функцией, но совершенно забыл. Woops. Спасибо за подробное объяснение. Тем не менее, интересен тот факт, что [] имеет более высокий приоритет, чем *.
bgomberg
@melpomene Интересно. Никогда не думал о том, sizeofчтобы стать унарным оператором.
mutantkeyboard
5
Вы не имеете в виду "... parses as sizeof (my_arr[0])"? Простое добавление пробела на самом деле ничего не меняет.
Бернхард Баркер
Я бы порекомендовал sizeof((my_array)[0])вместо этого
bolov
46

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

Муравей
источник
1
Первая версия существует для того, чтобы вы могли проверить размер целого числа в целом на машине (с тех времен, когда не было даже 64-битных машин!), Но intне является допустимым выражением, поэтому вы не можете использовать вторая форма с ней.
NH.
25

[]имеют более высокий приоритет, чем sizeof. Так sizeof(my_arr)[0]же, как sizeof((my_arr)[0]).

Вот ссылка на таблицу приоритетов.

МЧ
источник
8

Вы используете версию sizeofоператора, которая принимает выражение в качестве параметра. В отличие от того, что принимает тип, здесь не нужны скобки. Следовательно, операнд простой (my_arr)[0], круглые скобки лишние.

Quentin
источник