2017-01-29 15 views
4

Я пытаюсь научиться C, и я очень смущен.Различные выходные данные каждый раз в c?

#include <stdio.h> 

int main(void) 
{ 
    int a = 50000; 
    float b = 'a'; 
    printf("b = %f\n", 'a'); 
    printf("a = %f\n", a); 
    return 0; 
} 

Приведенный выше код производит различный выход каждый раз с gcc. Зачем?

+4

В первом 'printf' заявление, вы пытались напечатать' char' как 'float'. Это может быть частью дела. –

+10

Использование несоответствующего формата и типа аргумента - это * неопределенное поведение *. –

+0

Предупреждения вашего компилятора серьезны. – alk

ответ

1

%f спецификатор должен быть согласован с помощью параметра с плавающей точкой (float или double), вы придав ему int сек вместо этого. Не соответствует типу неопределенного поведения, то есть он может делать что угодно, включая печать различных результатов каждый раз.

Надеется, что это помогло, удач :)

Edit: Спасибо chqrlie за пояснение!

+2

'% f' фактически ожидает аргумент' float' или 'double'. Аргументы 'float' повышаются до' double' при передаче 'printf' (и любой другой вариационной функции). '' a'' не является 'char' в C, это значение' int'. Btw значение 'char' будет неявно продвигаться до' int' при передаче 'printf'. – chqrlie

1

Непредсказуемый Поведение.

При попытке печати с помощью значения формата несовпадение спецификатор, компилятор дать непредсказуемые output.The об использовании %f в качестве спецификатора формата для char и int не определено.

Используйте правильный спецификатор формата в программе по йо типа данных:

printf("%d\n", 'a'); // print ASCII value 
printf("%d\n", a); 
2

Вы передаете значение int («а») для формата %f ожидающей float или double. Это неопределенное поведение, которое может привести к разным результатам для каждого выполнения одной и той же программы. Второй printf имеет ту же проблему: %f ожидает float или double, но вы передаете значение int.

Вот исправленный вариант:

#include <stdio.h> 

int main(void) { 
    int a = 50000; 
    float b = 'a'; 

    printf("a = %d\n", a); 
    printf("b = %f\n", b); 
    printf("'a' = %d\n", 'a'); 

    return 0; 
} 

Выход:

a = 50000 
b = 97.000000 
'a' = 97 

Компиляция с большим количеством предупреждений включена, с аргументами командной строки -Wall -W -Wextra позволяет компилятору выполнять больше проверок на непротиворечивость и жалуются на возможные ошибки программирования , Он обнаружил бы ошибки в размещенном коде.

Действительно clang все еще жалуется на вышеприведенной коррекции:

clang -O2 -std=c11 -Weverything fmt.c -o fmt 
fmt.c:8:24: warning: implicit conversion increases floating-point precision: 'float' to 'double' [-Wdouble-promotion] 
    printf("b = %f\n", b); 
    ~~~~~~   ^
1 warning generated. 

b повышен до double при передаче printf(). Тип double имеет большую точность, чем тип float, который может выводить вводящие в заблуждение значения, если запрашиваются больше десятичных знаков, чем исходный тип.

Рекомендуется всегда использовать double для вычислений с плавающей точкой и зарезервируйте float типа для очень специфических случаев, когда лучше всего подходит, например, компьютерная графика API, некоторые бинарные форматы обмен ...

0

Прежде всего , gcc должен дать предупреждение для вышеуказанного кода. Это связано с несоответствием спецификатора формата.

Несовместимость форматирования (printf(), а также scanf()) даст непредсказуемое поведение в C. Это несмотря на то, что НКУ, как ожидается, Заботиться возможные преобразования типов, как int к float неявно.

Вот две хорошие ссылки.
https://stackoverflow.com/a/1057173/4954434
https://stackoverflow.com/a/12830110/4954434

После будет работать как expetced.

#include <stdio.h> 

int main(void) 
{ 
    int a = 50000; 
    float b = 'a'; 
    printf("b = %f\n", (float)'a'); 
    printf("a = %f\n", (float)a); 
    return 0; 
} 
1

С точки зрения реализации, передавая чисел с плавающей точкой (то что %f ожидает) в качестве переменных списков аргументов (то есть то, что ... означает printf прототип) и целых (то есть то, что 'a' является, в частности, из тип int) могут использовать разные регистры и макет памяти.

Обычно это определяется по ABI, вызывающим соглашения. В частности, в x86_64, %xmm0 будет считаться printf (с унифицированным значением), но gcc заполнит %rdi в printf.

См. Больше в System V Application Binary Interface AMD64 Architecture Processor Supplement, p. 56

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

0

Главное, что у вас есть, это тип данных, которые вы используете. Когда вы создаете переменную, вы указываете размер, который память должна резервировать для правильной работы. Например, char имеют размер 1 байт, int равный 4 байт и float 32 байт. Важно использовать один и тот же тип данных, чтобы не иметь непредсказуемого результата.

char a = 'a'; 
printf("%c",a); 

int b = 50000; 
printf("%d",b); 

float c = 5.7; 
printf("%f", c); 

Для получения дополнительной информации: C-Variables