Оператор sizeof
не оценивает свой аргумент, он только смотрит на тип своего операнда.
Допустим, у вас есть массив a
с типом "array [N] типа T". Тогда в большинстве случаев тип имени a
- это «указатель на T» (T *
), а значением указателя является адрес первого элемента массива (&a[0]
). То есть имя массива «распадается» на указатель на его первый элемент. «Распад» не происходит в следующих случаях:
- когда
a
используется с оператором address-of (&
),
- при инициализации
a
(присвоение массивов в C недопустимо) и
- когда
a
является операндом оператора sizeof
.
Итак, sizeof a
дает вам N
раз sizeof(T)
.
Когда вы делаете sizeof(a-3)
, тип операнда для sizeof
определяется выражением a-3
. Поскольку a
в a-3
используется в контексте значения (т. Е. Ни в одном из трех указанных выше контекстов), его типом является «указатель на int», а имя a
затухает до указателя до a[0]
. Таким образом, вычисление a-3
является неопределенным поведением, но поскольку sizeof
не оценивает его аргумент, a-3
используется только для определения типа операнда, поэтому с кодом все в порядке (более подробно см. Первую ссылку выше) ).
Исходя из вышесказанного, sizeof(a-3)
эквивалентно sizeof(int *)
, что на вашем компьютере равно 4.
«Преобразование» происходит из-за оператора вычитания. Вы можете увидеть похожий и, возможно, более удивительный результат с запятой:
printf("%zu\n", sizeof(1, a));
также напечатает sizeof(int *)
, поскольку оператор запятой приводит к a
использованию в контексте значения.