Рассмотрите эту программу
int main()
{
float f = 11.22;
double d = 44.55;
int i,j;
i = f; //cast float to int
j = d; //cast double to int
printf("i = %d, j = %d, f = %d, d = %d", i,j,f,d);
//This prints the following:
// i = 11, j = 44, f = -536870912, d = 1076261027
return 0;
}
Кто-то может объяснить, почему кастинг от двойного/плавающего до международных работ правильно в первом случае, и не работает при выполнении в printf?
Эта программа была скомпилирована на gcc-4.1.2 на 32-разрядной машине Linux.
Править: Ответ Zach кажется логичным, т.е. использование спецификаторов формата для выяснения, что появиться от стека. Однако затем полагайте, что это развивает вопрос:
int main()
{
char c = 'd'; // sizeof c is 1, however sizeof character literal
// 'd' is equal to sizeof(int) in ANSI C
printf("lit = %c, lit = %d , c = %c, c = %d", 'd', 'd', c, c);
//this prints: lit = d, lit = 100 , c = d, c = 100
//how does printf here pop off the right number of bytes even when
//the size represented by format specifiers doesn't actually match
//the size of the passed arguments(char(1 byte) & char_literal(4 bytes))
return 0;
}
Как это работает?
Функция printf
использует спецификаторы формата, чтобы определить, что выводить в стек. Поэтому когда она видит %d
, она вытаскивает 4 байта и интерпретирует их как int
, что неверно (двоичное представление (float)3.0
не то же самое, что (int)3
).
Вам придется либо использовать спецификаторы формата %f
, либо приводить аргументы к int
. Если вы используете достаточно новую версию gcc
, то включение более сильных предупреждений отлавливает такую ошибку:
$ gcc -Wall -Werror test.c
cc1: warnings being treated as errors
test.c: In function ‘main’:
test.c:10: error: implicit declaration of function ‘printf’
test.c:10: error: incompatible implicit declaration of built-in function ‘printf’
test.c:10: error: format ‘%d’ expects type ‘int’, but argument 4 has type ‘double’
test.c:10: error: format ‘%d’ expects type ‘int’, but argument 5 has type ‘double’
Ответ на отредактированную часть вопроса:
Правила продвижения целых чисел в Си гласят, что все типы меньше int
при передаче в качестве vararg переводятся в int
. Так что в вашем случае 'd'
повышается до int
, затем printf отрывает int
и приводит к char
. Лучшей ссылкой, которую я смог найти для этого поведения, была эта запись в блоге.
Поскольку вы не используете спецификатор формата с плавающей запятой, попробуйте:
printf("i = %d, j = %d, f = %f, d = %f", i,j,f,d);
В противном случае, если вы хотите 4 int, вы должны преобразовать их перед передачей аргумента для printf:
printf("i = %d, j = %d, f = %d, d = %d", i,j,(int)f,(int)d);
Причина, по которой ваш дополнительный код работает, заключается в том, что символьная константа повышается до int перед тем, как помещается в стек. Итак, printf выталкивает 4 байта для% c и для% d. Фактически, символьные константы имеют тип int, а не тип char. C в этом смысле странный.
Ответ Джека объясняет, как решить вашу проблему. Я объясню, почему вы получаете неожиданные результаты. Ваш код эквивалентен:
float f = 11.22;
double d = 44.55;
int i,j,k,l;
i = (int) f;
j = (int) d;
k = *(int *) &f; //cast float to int
l = *(int *) &d; //cast double to int
printf("i = %d, j = %d, f = %d, d = %d", i,j,k,l);
Причина в том, что f
и d
передаются в printf
как значения, а затем эти значения интерпретируются как ] int
с. Это не меняет двоичное значение, поэтому отображаемое число является двоичным представлением float
или double
. Фактическое приведение float
к int
намного сложнее в сгенерированной сборке.
printf использует списки аргументов переменной длины, что означает, что вам нужно предоставить информацию о типе. Вы предоставляете неверную информацию, поэтому она путается. Джек предлагает практическое решение.
Нет такой вещи, как «приведение к int
в printf
». printf
не выполняет и не может выполнять приведение типов. Несогласованный спецификатор формата приводит к неопределенному поведению.
На практике printf
просто получает необработанные данные и повторно интерпретирует их как тип, подразумеваемый спецификатором формата. Если передать ему значение double
и указать спецификатор формата int
(например, % d
), printf
примет это double
значение и слепо переинтерпретировать его как int
. Результаты будут совершенно непредсказуемыми (поэтому формально это приводит к неопределенному поведению в C).