2010-02-05 2 views
12

Рассмотрим следующий код: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() от типа массива к целочисленному типу?

+3

Второй на самом деле печатает 'sizeof (int *)', а не 'sizeof (int)'. 'sizeof (int *)' просто бывает 4 на вашей платформе. – AnT

+0

Кроме того, правильный способ печати значения 'size_t' (значение, возвращаемое' sizeof') с ''% zu'', если ваш компилятор поддерживает эту особенность C99. Если нет, лучше сделать бросок на «unsigned» или «unsigned long». –

ответ

30

Оператор sizeof не оценивает свой аргумент, он только looks at the type своего операнда.

Предположим, у вас есть массив a с типом «массив [N] типа T». Затем в большинстве случаев тип имени a является «указателем на T» (T *), а значение указателя является адресом первого элемента массива (&a[0]). То есть имя массива «распадается» на указатель на его первый элемент. «Затухающих» не происходит в следующих случаях:

  • когда a используется с Адресов (&) оператора,
  • в инициализации a (это незаконно присвоить массивы в C) и
  • , когда a является операндом оператора sizeof.

Таким образом, sizeof a дает вам Nsizeof(T).

Когда вы делаете sizeof(a-3), тип операнда sizeof определяется выражением a-3. Поскольку a в a-3 используется в value context (т. Е. Ни один из трех контекстов выше), его тип является «указателем на int», а имя a распадается на указатель на a[0]. Таким образом, вычисление a-3 является неопределенным поведением, но поскольку sizeof не оценивает его аргумент, a-3 используется только для определения типа операнда, поэтому код в порядке (см. Первую ссылку выше для более).

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

«Преобразование» связано с оператором вычитания. Вы можете увидеть подобное, и, возможно, более удивительным, результат с оператором запятая:

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

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

+0

+1 Гораздо понятнее, чем у меня. –

5

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

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

+6

Чтобы быть ясным - результат 'sizeof' не является константой времени компилятора, только когда операнд является массивом переменной длины. Он по-прежнему является константой для других типов операндов. –

1

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

1

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

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

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

+0

@David: до тех пор, пока результат не переполняется, если вы производите выражение как '(int)', то его печать с '"% d "' в порядке. В этом случае почти наверняка размер будет соответствовать «int». В C99 можно было бы использовать ''% zu''. Для C89 ваше предложение лучше, чем печать с '"% d "' после кастинга как '(int)', потому что она более подвержена переполнению. –

+0

@Alok: Верно, но делать кастинг гораздо важнее, чем то, что вы бросаете. Кроме того, я очень хорошо знаком с C89 и не использовал C99, и это, вероятно, показывает. –

+0

У ОП есть бросок в его вопросе. Я согласен с вами - без трансляции, '"% d "' неверно, но вызов 'printf()' OP в порядке, пока переполнение не происходит. –

Смежные вопросы