2014-10-03 2 views
4
#include <stdio.h> 

union p{ 
    int x; 
    float y; 
}; 

int main() 
{ 
    union p p; 
    p.x = 10; 
    printf("%f\n", p.y); 
    return 0; 
} 

Выход:печать переменной объединение - странное поведение

0,000000

Когда я пытаюсь скомпилировать выше программ, она не показывает каких-либо предупреждений, даже в основной функция. Почему printf не печатает значение 10.00000?

Я прочитал некоторые связанные вопросы по StackOverflow который объясняет поведение printf при печати целого числа без типажей с поплавком спецификатором, но я думаю, что здесь другой случай. Я печатаю число с плавающей точкой с помощью спецификатора float. Он должен напечатать правильное значение. Может ли кто-нибудь объяснить, что здесь происходит?

+2

Поскольку представление памяти для int и поплавка отличается (проверьте с вашим отладчиком, и вы увидите). Кроме того, запись p.x и доступ к p.y - это неопределенное поведение ... –

+1

@AdrianoRepetti; 'accessing p.y - неопределенное поведение': Нет. В этом случае нет. – haccks

+1

@haccks AFAIK это не так, если 'int' и' float' имеют одинаковый размер (фактически, если они являются _compatible types_), но они не требуются (и здесь это неизвестно, потому что это зависит от среды). Если я не ошибаюсь ... –

ответ

4

Вы положили 10 целых чисел в x. если вы пишете p.x = 1092616192 вместо p.x = 10 вы увидите, что происходит также прочитать https://en.wikipedia.org/wiki/IEEE_floating_point

10 не равен 10.f в памяти.

+2

Это предполагает, что 'int' - 4 байта, что может быть не так –

+0

... и если это не приведет к UB. –

+0

, если вы хотите быть педантичным, нет гарантийного плавания, также будет 4 байта. Однако на всех распространенных архитектурах на данный момент есть int и float длиной 4 байта. BTW даже лучше ссылку, чтобы увидеть 4-байтовый макет float: https://en.wikipedia.org/wiki/Single-precision_floating-point_format –

5

Это связано с тем, что объекты int и float имеют разные двоичные представления. Предполагая, что 32-разрядные int и float, прямой порядок байтов и IEEE-754 представления, то есть двоичный шаблон, как:

0000 1010 0000 0000 0000 0000 0000 0000 

что (в контексте число с плавающей) очень небольшое количество, которое напечатано значение в 0.000000 с использованием %f (можно сказать, что округляется на printf). Вы можете попробовать с %e фс, что приводит к другой выход:

1.401298e-44 

C99 введен %a фс, которая представляет собой плавающую-номер точно так, как она хранится, а именно, как 2-щелочное число с плавающей точкой (т.е. с мантиссы и экспоненты):

0x1.4p-146 
3

INT и поплавок, имеющие различные двоичные представления, если вы пытались печатать в научном формате %e вы увидите следующее значение для p.f:

1.401298e-44

так предполагая IEEE 754 single precision float и 4 байт INT, давайте попробуем float convertor, чтобы увидеть, что действительное шестнадцатеричное значение будет для, скажем, значение 40 в IEEE 754 формат.

Подключив в 40 в преобразователь дает шестнадцатеричное значение:

0x42200000

если мы назначаем это значение p.x:

p.x = 0x42200000; 

вы увидите значение 40 обратно при печати p.y, see it live.

Это всегда дискуссия о том, является ли тип punning через объединение неопределенным поведением, насколько я понимаю, это не так, и на практике компиляторы явно поддерживают этот тип типа punning, вот the gcc reference explaining its support.

Смежные вопросы