sizeof ведет себя неожиданно
Рассмотрим следующий код:
#include <stdio.h>
int main(void)
{
int a[10];
printf("%d",(int)sizeof(a)); //prints 10*sizeof(int) (40 on my compiler)
printf("%d",(int)sizeof(a-3)); //prints sizeof(int) (4 on my compiler)
}
я знаю это sizeof()
является оператором времени компиляции, но я был удивлен, увидев вывод второго printf()
, Что может быть причиной? Есть ли неявное преобразование аргумента sizeof()
из типа массива в целочисленный тип?
4 ответа
sizeof
оператор не оценивает свой аргумент, он только смотрит на тип своего операнда.
Допустим, у вас есть массив a
с типом "массив [N] типа T". Затем, в большинстве случаев, тип имени a
это "указатель на T" (T *
), а значением указателя является адрес первого элемента массива (&a[0]
). То есть имя массива "распадается" на указатель на его первый элемент. "Разложение" не происходит в следующих случаях:
- когда
a
используется с адресом (&
) оператор, - в инициализации
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
привыкнуть в контексте значения.
(a-3)
имеет тип int*
и печатает тебя sizeof(int*)
который 4 на вашей платформе.
И обратите внимание, что sizeof()
больше не является константой времени компиляции в C99 (из-за массивов переменной длины).
Нет, во втором случае аргумент интерпретируется как int*
указатель, который также имеет размер 4 на вашем компьютере.
sizeof()
возвращает размер типа, поэтому важен тип.
Это также не должно быть напечатано с %d
, По крайней мере, явно приведите его к unsigned long
или же unsigned long long
и используйте соответствующий спецификатор формата. Когда я преподавал C, у меня был студент, который получил неправильный ответ, напечатав size_t
с %d
как ошибочно сказано в учебнике.
Тем не мение, a
тип массива В C типы массивов переходят в типы указателей, если вы делаете с ними почти все или громко чихаете, поэтому почти все, что вы делаете для a
даст тип указателя. Как вы узнали, добавление или вычитание числа будет затухать. (В конце концов, массив не может быть использован в арифметике, но указатель может.)