Почему ответ sizeof отличается от %f?

Почему я не получаю то же значение, что и с %d, а не с %f?

void main()
{ 
   int arr;
   printf("%f",sizeof(arr));
}

Выход: 2.168831

void main()
{ 
   int arr;
   printf("%d",sizeof(arr));
}

вывод: 4


person Shanpriya    schedule 12.10.2013    source источник
comment
printf("%f",sizeof(arr)); не определено, второе не использовать void main использовать int main   -  person Grijesh Chauhan    schedule 12.10.2013
comment
Потому что %f ожидает double, а вы передаете size_t. Это неопределенное поведение.   -  person    schedule 12.10.2013
comment
@GrjeshChauhan, почему ты говоришь, что это не определено? Вы имеете в виду, что %f ожидает только двойное значение?   -  person Shanpriya    schedule 12.10.2013
comment
@Shanpriya читайте ниже обновленный ответ.   -  person Grijesh Chauhan    schedule 12.10.2013
comment
@Shanpriya Хороший справочник C Printf и Scanf Reference   -  person Grijesh Chauhan    schedule 12.10.2013
comment
Вы солгали компилятору. Вы заявили, что я дам вам двойное значение с %f, но затем дали беззнаковый целочисленный тип в качестве аргумента. Это неопределенное поведение, и компиляторам разрешено даже отказываться от компиляции такого кода.   -  person Jens    schedule 13.10.2013


Ответы (3)


sizeof возвращает size_t, но printf("%f",... сообщает printf, что следующий аргумент имеет тип double. size_t имеет другое расположение в памяти, чем double, поэтому printf неправильно интерпретирует этот аргумент.

Как указывает Грижеш, правильным спецификатором формата будет %zu, но если вы действительно хотите использовать %f

printf("%f",(double)sizeof(arr));

должно сработать

person simonc    schedule 12.10.2013
comment
и правильная строка формата %zu для sizeof (в mcrosoft используется %lu) - person Grijesh Chauhan; 12.10.2013
comment
так что %zu используется для определения sizeof, и даже %d - неправильный способ объявления? - person Shanpriya; 12.10.2013
comment
@simonc Я думаю, добавить "If a conversion specification is invalid, the behavior is undefined" из C standard (6.5.2.2 paragraph 6) ОП сомневается, когда он прокомментировал мне . - person Grijesh Chauhan; 12.10.2013

Чтобы дать немного больше пояснений, это потому, что printf() является функцией с переменным числом аргументов, и типы аргументов неизвестны до тех пор, пока функция не запустится и не определит их из строки формата. Это означает, что обычные неявные преобразования не будут выполняться.

Например, если вы делаете что-то вроде:

void myfunc(double d) {
    /*  Do something with d  */
}

int main(void) {
    int n = 5;
    myfunc(n);
    return 0;
}

тогда компилятор знает, что myfunc() принимает аргумент типа double, и поэтому может и будет неявно преобразовывать n из int в double перед передачей параметра.

Когда вы делаете:

int main(void) {
    double f = 5.5;
    printf("%d\n", f);  /*  WARNING: buggy code  */
    return 0;
}

компилятор понятия не имеет, какой тип ожидает printf(), пока не запустится и не поймет %d в строке формата. Итак, все, что он может сделать, это передать аргумент double как double, а когда printf() запустится и увидит %d, он интерпретирует его как int, и все пойдет ужасно неправильно, когда он неправильно интерпретирует битовый шаблон (и, в этом случай, вероятно, размер аргумента, а также отбрасывание его для любых других аргументов, которые идут после него).

Это, среди прочего, причина, по которой эти функции являются одним из тех редких случаев, когда приведение к void * действительно необходимо в C, поскольку указатели на разные типы объектов могут быть разных размеров, и поскольку %p ожидает void *, вы должны передать ему void *, а не указатель на любой другой тип объекта.

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

person Crowman    schedule 12.10.2013

Оператор sizeof возвращает `size_t Вы должны использовать спецификатор формата %zu с printf()

Обратите внимание, что size_t — это unsigned, размеры signed могут быть представлены как ssize_t.

Оба эти типа определены в заголовке stddef.h

§7.21.6.1 p9 стандарта C11 говорит

Если спецификация преобразования недействительна, поведение не определено. Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования, поведение не определено.

person Gangadhar    schedule 12.10.2013