То, как имеет преобразование плавающие/двойные к интервалу, обработало в printf?

Рассмотрите эту программу

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;
}

Как это работает?

10
задан Community 23 May 2017 в 12:08
поделиться

6 ответов

Функция 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. Лучшей ссылкой, которую я смог найти для этого поведения, была эта запись в блоге.

19
ответ дан 3 December 2019 в 14:24
поделиться

Поскольку вы не используете спецификатор формата с плавающей запятой, попробуйте:

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);
2
ответ дан 3 December 2019 в 14:24
поделиться

Причина, по которой ваш дополнительный код работает, заключается в том, что символьная константа повышается до int перед тем, как помещается в стек. Итак, printf выталкивает 4 байта для% c и для% d. Фактически, символьные константы имеют тип int, а не тип char. C в этом смысле странный.

1
ответ дан 3 December 2019 в 14:24
поделиться

Ответ Джека объясняет, как решить вашу проблему. Я объясню, почему вы получаете неожиданные результаты. Ваш код эквивалентен:

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 намного сложнее в сгенерированной сборке.

4
ответ дан 3 December 2019 в 14:24
поделиться

printf использует списки аргументов переменной длины, что означает, что вам нужно предоставить информацию о типе. Вы предоставляете неверную информацию, поэтому она путается. Джек предлагает практическое решение.

0
ответ дан 3 December 2019 в 14:24
поделиться

Нет такой вещи, как «приведение к int в printf ». printf не выполняет и не может выполнять приведение типов. Несогласованный спецификатор формата приводит к неопределенному поведению.

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

8
ответ дан 3 December 2019 в 14:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: