Aktualizacja: po zrobieniu kilka badań na ten temat, wydaje się, że różnice między reprezentacjami pamięci float
i int
nie są odpowiedzialni za zachowanie tych trzech programów.
Sprawdziłem kod obiektowy dla trzeciego programu i znalazłem przyczynę dziwnego zachowania: argumenty zmiennoprzecinkowe są wysyłane do innego rejestru/stosu niż liczby całkowite. I printf
polega na tym i wyszukuje je w innych lokalizacjach niż te używane przez adresata printf
(tj. Metoda main
) umieszcza argumenty.
Oto odnośny demontaż trzeci program (dla architektury x86_64):
0000000100000f18 leaq 0x71(%rip), %rdi ## literal pool for: "b = %f\n"
0000000100000f1f movsd 0x61(%rip), %xmm0 ## b variable gets sent to xmm0
0000000100000f27 movl $0x0, -0x4(%rbp)
0000000100000f2e movb $0x61, -0x5(%rbp) ## a variable gets placed on the callee stack
0000000100000f32 movsd %xmm0, -0x10(%rbp)
0000000100000f37 movsd -0x10(%rbp), %xmm0
0000000100000f3c movb $0x1, %al
0000000100000f3e callq 0x100000f66 ## symbol stub for: _printf
0000000100000f43 leaq 0x4e(%rip), %rdi ## literal pool for: "a = %f\n"
0000000100000f4a movsbl -0x5(%rbp), %esi
0000000100000f4e movl %eax, -0x14(%rbp)
0000000100000f51 movb $0x0, %al
0000000100000f53 callq 0x100000f66 ## symbol stub for: _printf
I printf
polega na tym, że zakłada, że wywoływany umieścił %f
argumenty w xmm0
/xmm1
/etc rejestrów, i zachowanie trzech programów jest tak:
- w pierwszym programie
printf
poszukuje %f
argumentu w xmm0 zarejestrować Howe ver jak jesteśmy na początku programu, rejestr jest czysty i main
umieścił a
do eax
, więc xmm0
posiada wartość zero, a to co printf
drukuje
- w drugim programie
main
poprawnie umieszcza b
w xmm0
i printf
zabiera go stamtąd, drukowanie poprawną wartość
- w trzecim programie ze względu na fakt, że
b
jest drukowany jako pierwszy rejestr xmm0
obejmie tę wartość, a ponieważ printf
nie robi bałagan w rejestrze, kiedy to drugi raz wywołuje go ponownie z xmm0
, który pozostał nienaruszony ter pierwsze wywołanie printf
.
Tak więc chodzi o konwencje wywołującego/wywołującego, w których liczby całkowite i przepływy są wysyłane przez dzwoniącego i skąd odbiorca próbuje je odebrać.
Oryginalny odpowiedź: W pierwszym programie, który próbujesz wydrukować pływaka, ale trzeba zdać int (char jest mniejszy int). Z uwagi na to, że int i float mają różne reprezentacje binarne, int 97 (odpowiadający znakowi "a") odpowiada bardzo niewielkiemu floatowi: 1.36E-43, który zostaje wydrukowany jako zero.
Tutaj jest binarna reprezentacja 97 (kompilator rozszerza węgiel odbarwiający 1 bajt argumentu 4 bajtów podczas wywoływania funkcji)
00000000 00000000 00000000 01100001
IEEE 754 jest standardowym formatem binarnym pływaka/podwójnego Liczby można grać za pomocą konwertera online here, a można zobaczyć, jak ta sama liczba binarna ma różne wartości, gdy jest interpretowana jako int lub float.
RTFM, np. przeczytaj [printf (3)] (http://man7.org/linux/man-pages/man3/printf.3.html) i/lub [printf] (http://en.cppreference.com/w/c/io/fprintf) –
Nie masz pojęcia o printf, ale 'float b = 'a';' jest w porządku dla ciebie? ;) – sebastian
@sebastian przepraszam, nie wiem co masz na myśli. –