Подтвердить что ты не робот

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() от типа массива к целочисленному типу?

4b9b3361

Ответ 1

Оператор 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", а name a распадается на указатель на a[0]. Таким образом, вычисление a-3 является undefined поведением, но поскольку sizeof не оценивает его аргумент, a-3 используется только для определения типа операнда, поэтому код в порядке (см. Первую ссылку выше для более).

Из вышесказанного, sizeof(a-3) эквивалентен sizeof(int *), который равен 4 на вашем компьютере.

"Преобразование" происходит из-за оператора вычитания. Вы можете увидеть аналогичный и, возможно, более неожиданный результат с помощью оператора запятой:

printf("%zu\n", sizeof(1, a));

также напечатает sizeof(int *) из-за оператора запятой, в результате чего a будет использоваться в контексте значений.

Ответ 2

(a-3) имеет тип int*, и он печатает вас sizeof(int*), который равен 4 на вашей платформе.

И обратите внимание, что sizeof() больше не является константой времени компиляции в C99 (из-за массивов с переменной матрицей).

Ответ 3

Нет, во втором случае аргумент интерпретируется как указатель int*, который также имеет размер, равный 4 на вашем компьютере.

Ответ 4

sizeof() возвращает размер типа, поэтому тип является тем, что важно.

Также не следует печатать с %d. По крайней мере, явным образом добавьте его в unsigned long или unsigned long long и используйте соответствующий спецификатор формата. Когда я преподавал C, у меня был ученик, который получил неправильный ответ, напечатав size_t с помощью %d как ошибочно сказал учебник.

В любом случае a - это тип массива. В C типы массивов распадаются на типы указателей, если вы делаете что-либо с ними или громко чихаете, поэтому почти все, что вы делаете с a, даст тип указателя. Как вы узнали, добавление или вычитание числа распадется. (В конце концов, массив не может использоваться в арифметике, но указатель может.)