Как мне остановить printf, выдающий разные значения в зависимости от порядка? - PullRequest
1 голос
/ 13 апреля 2011

Код:

#include <stdio.h>

int main()
{
    printf(
            " %f, %u, %x,\n", 1.0f, 1.0f, 1.0f);
    return 0;
}

Выход: 1.000000, 1072693248, 0,

Код:

#include <stdio.h>

int main()
{
    printf(
            " %x, %f, %u,\n", 1.0f, 1.0f, 1.0f);
    return 0;
}

Выход: 3ff00000, 0.000000, 0

Код:

#include <stdio.h>

int main()
{
    printf(
            " %x, %u, %f,\n", 1.0f, 1.0f, 1.0f);
    return 0;
}

Вывод: 3ff00000, 0, 1.000000

Это просто проблема с количеством байтов, которые% u и% x потребляют, и как мне получитьзначения становятся согласованными?

Ответы [ 5 ]

3 голосов
/ 13 апреля 2011

Передача аргументов, тип которых не соответствует соответствующему компоненту строки формата, приведет к неопределенным результатам, как вы заметили. В частности, "%x" и "%u" оба ожидают значения типа (без знака) int. Передача float (которая часто фактически представляется как double или long double в зависимости от ABI) семантически неверна.

Ваш компилятор должен предупреждать вас об этом - если нет, убедитесь, что у вас включены все предупреждения (-Wall для GCC).

Если вы хотите 1.0f как целое число, просто приведите:

printf(" %x, %u, %f\n", (unsigned)1.0f, (unsigned)1.0f, 1.0f);

Если вы пытаетесь получить двоичные представления, попробуйте что-то вроде этого:

float a = 1.0f;
printf(" %x, %u, %f\n", *((unsigned*)&a), *((unsigned*)&a), a);
3 голосов
/ 13 апреля 2011

Вы указываете printf, какие аргументы ожидать, через первый параметр: строку формата .Дополнительные значения, которые вы передаете printf , должны соответствовать строке формата.Если этого не произойдет, вы получите вывод мусора.В ваших примерах вы передаете значение с плавающей запятой 1.0f и говорите printf, что следует рассматривать его как целое число со спецификатором формата %u.Таким образом, вы получаете вывод мусора.

Справочная страница для printf содержит все детали, необходимые для создания правильной строки формата для значений, которые вы хотите напечатать.

2 голосов
/ 13 апреля 2011

Вы не можете.

Каждое поле формата ожидает тот же тип, который указан спецификатором формата.

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

В вашем случае вы вызываете printf с 3 значениями с плавающей запятой, но значения с плавающей запятой увеличиваются в два раза (см. http://en.wikipedia.org/wiki/Type_conversion#Type_promotion_in_C-like_languages).
Таким образом, в этом случае (в системах x86) в стеке будет 3 раза по 8 байт.

Первое поле формата будет принимать ожидаемое количество байтов в стеке:

  • % x займет 4 байта
  • % u также займет 4 байта
  • % f займет 8 байтов

Таким образом, порядок полей формата будет влиять на фактические данные, которые печатаются. (при использовании% f даже возможно, что значение с плавающей точкой является недопустимым, что может привести к сбою).

Если вы хотите узнать больше о списках переменных аргументов, посмотрите ответы в Передача переменных аргументов в C

1 голос
/ 13 апреля 2011

Когда я компилирую это с помощью gcc, я получаю следующие предупреждения:

crazy.c: In function ‘main’:
crazy.c:4: warning: format ‘%u’ expects type ‘unsigned int’, but argument 3 has type ‘double’
crazy.c:4: warning: format ‘%x’ expects type ‘unsigned int’, but argument 4 has type ‘double’
crazy.c:5: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘double’
crazy.c:5: warning: format ‘%u’ expects type ‘unsigned int’, but argument 4 has type ‘double’
crazy.c:6: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘double’
crazy.c:6: warning: format ‘%u’ expects type ‘unsigned int’, but argument 3 has type ‘double’

Вот почему вы получаете вывод мусора, который вы используете. Хорошая идея - составлять самые строгие возможные предупреждения, чтобы вы могли заблаговременно уведомить о таких проблемах.

%u и %x являются спецификаторами формата, которые ожидают целые числа без знака. Вы должны использовать спецификаторы с плавающей запятой %f для всех примеров. Это всегда будет давать согласованные результаты с учетом чисел с плавающей запятой.

0 голосов
/ 13 апреля 2011

Это проблема со списком аргументов, которые не соответствуют дескрипторам формата.Если вы хотите напечатать 1.0f как целочисленное значение, сначала преобразуйте его в целое число:

printf("%x, %u, %f,\n", (int)1.0f, (int)1.0f, 1.0f);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...