Po pierwsze, jak poruszył w kilku innych odpowiedzi, ale nie, moim zdaniem, wypisanym wystarczająco jasno: to robi pracę, aby zapewnić całkowitą w większości kontekstów gdzie funkcja biblioteki zajmuje double
lub float
argument. Kompilator automatycznie wstawi konwersję. Na przykład, sqrt(0)
jest dobrze zdefiniowany i będzie się zachowywał dokładnie tak sqrt((double)0)
, jak i to samo dotyczy każdego innego wyrażenia typu całkowitego tam użytego.
printf
jest inny. Jest inaczej, ponieważ wymaga zmiennej liczby argumentów. Jego prototypem funkcji jest
extern int printf(const char *fmt, ...);
Dlatego kiedy piszesz
printf(message, 0)
kompilator nie ma żadnych informacji o tym, jaki typ printf
oczekuje tego drugiego argumentu. Ma tylko typ wyrażenia argumentu, którym jest int
. Dlatego, w przeciwieństwie do większości funkcji bibliotecznych, to ty, programista, musisz upewnić się, że lista argumentów jest zgodna z oczekiwaniami ciągu formatu.
(Nowoczesne kompilatory mogą zajrzeć do ciągu formatu i powiedzieć, że masz niezgodność typów, ale nie zaczną wstawiać konwersji, aby osiągnąć to, co miałeś na myśli, ponieważ lepszy kod powinien się teraz zepsuć, kiedy zauważysz , niż lata później, gdy został przebudowany za pomocą mniej pomocnego kompilatora).
Teraz druga połowa pytania brzmiała: biorąc pod uwagę, że (int) 0 i (float) 0.0 są, w większości nowoczesnych systemów, reprezentowane jako 32 bity, z których wszystkie są zerowe, dlaczego i tak nie działa to przypadkowo? Standard C mówi tylko, że „to nie jest wymagane do pracy, jesteś sam”, ale pozwól mi przeliterować dwa najczęstsze powody, dla których to nie zadziała; to prawdopodobnie pomoże ci zrozumieć, dlaczego nie jest to wymagane.
Po pierwsze, z powodów historycznych, kiedy przechodzisz float
przez zmienną listę argumentów, jest ona promowana do double
, która w większości nowoczesnych systemów ma 64 bity. Zatem printf("%f", 0)
przekazuje tylko 32 bity zerowe do wywoływanego, oczekując 64 z nich.
Drugim, równie istotnym powodem jest to, że argumenty funkcji zmiennoprzecinkowych mogą być przekazywane w innym miejscu niż argumenty liczb całkowitych. Na przykład większość procesorów ma oddzielne pliki rejestrów dla liczb całkowitych i wartości zmiennoprzecinkowych, więc może być regułą, że argumenty od 0 do 4 trafiają do rejestrów od r0 do r4, jeśli są liczbami całkowitymi, ale od f0 do f4, jeśli są zmiennoprzecinkowe. Więc printf("%f", 0)
szuka w rejestrze f1 tego zera, ale w ogóle go tam nie ma.
printf
oczekuje adouble
, a ty dajesz muint
.float
iint
może mieć ten sam rozmiar na twoim komputerze, ale w0.0f
rzeczywistości jest konwertowany na adouble
po umieszczeniu na wariadycznej liście argumentów (iprintf
oczekuje tego). Krótko mówiąc, nie wywiązujesz się ze swojej części umowyprintf
na podstawie specyfikacji, których używasz, i argumentów, które podajesz.