2016-07-08 21 views
5

Próbowałem wydrukować przy użyciu pacy %d (wiem, że to nie powinno być zrobione. Ale za każdym razem ponownie uruchomić wykonywalny daje się inną wartość)
Moje pytanie to: Dlaczego drukowana wartość zmienia się za każdym razem? mój system: Ubuntu 14.04 (64 bit) Compiler: 4.8.4 Oto kod:C zmiennoprzecinkowej zmienia wartość% d za każdym razem wykonywany

#include<stdio.h> 

int main(){ 
    float b = 12.3456; 

    printf("%d\n",b); 

} 

Przykładowe wyjście:

[email protected]:~/C-fi$ ./test 
-1629995944 
[email protected]:~/C-fi$ ./test 
1147348376 
[email protected]:~/C-fi$ ./test 
-1746005432 
[email protected]:~/C-fi$ ./test 
510102216 
[email protected]:~/C-fi$ 
+8

Wspaniały przykład * niezdefiniowanego zachowania * - Czego jeszcze potrzebujesz? – tofro

+0

Chcę wiedzieć, dlaczego to się stało? – 4bh1

+0

Ponieważ jest niezdefiniowany? – tofro

Odpowiedz

7

Prawdopodobnie wartość zmiennoprzecinkowa jest przekazywana poprzez rejestr FPU, ale printf() próbuje odczytać liczbę całkowitą z innego miejsca, gdzie spodziewa się zobaczyć liczbę całkowitą (stos lub inny rejestr). Treść tego miejsca jest nieokreślona. Ściśle mówiąc (jak wspomniał @dbush) twój kod powoduje niezdefiniowane zachowanie.

+0

To nie odpowiada żadnemu ABI Wiem, ale niektóre ABI przechodzą wartości zmiennoprzecinkowe, które mają być drukowane z '% f' i wartościami całkowitymi, które mają być drukowane z'% d' zarówno w rejestrach, co może wyjaśnić, co widzimy w zależności od poprzedniego kodu (i ASLR). –

+1

np. dla x64, https://msdn.microsoft.com/en-us/library/ms235286.aspx mówi "Argumenty są przekazywane w rejestrach RCX, RDX, R8 i R9 .Jeśli argumenty są float/double, są one przekazywane w XMM0L, XMM1L, XMM2L i XMM3L. " Tak więc printf spodziewa się float w XMM0L, ale ten rejestr nigdy nie został ustawiony przez twój kod. – moonshadow

+1

@PascalCuoq SysV ABI dla amd64 wygląda następująco. – fuz

8

Używanie niewłaściwego specyfikatora formatu dla printf jest klasycznym przykładem undefined behavior. Oznacza to, że zachowanie jest nieprzewidywalne i nie można na nim polegać, aby być konsekwentnym od jednego uruchomienia do drugiego. Może się zawiesić, może wydrukować losowe wartości lub może działać.

To, co się dzieje, to szczegół implementacji danego kompilatora i architektury. Ale chyba, że ​​faktycznie piszesz kompilator, nie ma większego pożytku z wkopywania się w niego.

Jeśli znasz asembler, możesz spojrzeć na skompilowany kod, aby zobaczyć, jakie instrukcje wygenerował. Należy jednak pamiętać, że różne kompilatory (nawet ten sam kompilator z różnymi ustawieniami optymalizacji) najprawdopodobniej wygenerują inny zestaw.

1

Jak wskazano w komentarzach, brzmi to jak niezdefiniowane zachowanie , które wyjaśnia (no, w pewnym sensie), co się dzieje.

W przypadku, gdy chcesz przeczytać wewnętrznej reprezentacji pływaka, lub chcesz zrobić kilka hacków niskopoziomowe od liczby, nie ma prostych sposobów, aby to zrobić, na przykład:

union { 
    float f; 
    int i; 
} n; 

n.f = 12.3456; 
printf("%d\n", n.i); // should be the same number each time 

Należy pamiętać, że działa to tylko wtedy, gdy float imają ten sam rozmiar w systemie, ale to powinno być w przypadku większości ludzi. Jeśli chcesz się upewnić, możesz dodać assert(sizeof(float) == sizeof(int)).

+0

Ściśle mówiąc, ten hack "union" jest nieprzenośny, przynajmniej dlatego, że pola mogą się nie pokrywać z powodu różnych reguł wyrównania. Byłoby lepiej zrobić to za pomocą 'memcpy()'. – Sergio

+1

@Serhio Na początku "unii" nigdy nie ma dopełnienia, więc dlaczego członkowie nie będą się nakładać? ISO/IEC 9899: 2011 § 6.7.2.1 (15) '... Może być nienazwane dopełnienie wewnątrz obiektu konstrukcji, ale nie na jego początku." Trzeba przyznać, że nie wspomina się o obiektach unii. –

+0

W nawiązaniu do mojego poprzedniego komentarza, §6.5.3.4 (4) o 'sizeof':' ... Kiedy zastosowany do operanda, który ma strukturę lub typ złączki, wynikiem jest całkowita liczba bajtów w takim obiekcie, w tym wewnętrznym i końcowe dopełnianie. "Nie wspomina o dopełnieniu wiodącym, co sugeruje, że nie istnieje. –

Powiązane problemy