2 TYPY. OPERATORY I WYRAŻENIA
• Następnie, jeśli którykolwiek z argumentów ma kwalifikator long, to ten drugi zostanie przekształcony do long.
Zauważ, że w wyrażeniu argumenty typu float nie są automatycznie przekształcane do double - jest to zmiana w stosunku do pierwotnej definicji języka. Zazwyczaj z podwójnej precyzji będą korzystać funkcje matematyczne, takie jak zdefiniowane w pliku nagłówkowym <math.h>. Głównym powodem posługiwania się obiektami typu float jest oszczędność pamięci przy rezerwowaniu dużych tablic lub - rzadziej - oszczędność czasu przy działaniu na maszynach, w których arytmetyka podwójnej precyzji jest szczególnie kosztowna.
Reguły przekształceń są bardziej skomplikowane dla argumentów unsigned. Problem polega na tym, że skutek porównania dwóch obiektów, z których jeden jest liczbą ze znakiem, a drugi bez, zależy od maszyny. Porównania takie zależą bowiem od długości reprezentacji różnych typów całkowitych. Załóżmy na przykład, że typ int zajmuje 16 bitów, a typ long 32 bity. Wtedy -1L < 11), ponieważ argument 11) jest typu int. zatem w wyrażeniach jest promowany do signed long. Jednocześnie zaś -1L > 1 UL. gdyż argument -1L jest promowany do unsigned long i wobec tego ma wygląd wielkiej liczby dodatniej.
Przekształcenia zachodzą również w przypisaniach: wartość prawej strony jest przekształcana do typu lewej strony, który będzie typem wyniku.
Dłuższe liczby całkowite są przekształcane do krótszych przez odrzucenie wystających bardziej znaczących bitów. Zatem po przypisaniach
c = i; 1
Jeśli x jest typu float, a i jest typu int, to w obu przypisaniach: x = i oraz i = x nastąpi? odpowiednie przekształcenia typów; zamiana float na int spowoduje przy tym obcte* cie części ułamkowej. Od implementacji zależy, czy przy przekształcaniu do flOa! obiektu typu double jego wartość zostanie zaokrąglona czy obcięta.
2.7 PRZEKSZTAŁCENIA TYPÓW
Każdy argument funkcji jest wyrażeniem, toteż przy przekazywaniu gumentów również zachodzą przekształcenia typów. Gdy nie podano prototypu funkcji, wówczas typy char i short stają się int, a typ float staje się double. Właśnie z tej przyczyny argumenty funkcji deklarowaliśmy jako int lub double nawet wtedy, kiedy wywoływaliśmy ją z argumentami char lub float.
Na koniec, w dowolnym wyrażeniu można jawnie wymusić przekształcenie typów za pomocą jednoargumentowego operatora zwanego rzutem (ang. cast). W konstrukcji
(nazwa-typu) wyrażenie
wyrażenie jest przekształcane według podanych reguł do typu określonego przez nazwa-typu. Dokładniej mówiąc, rzut działa tak, jak gdyby wartość wyrażenia przypisano zmiennej wskazanego typu, a następnie użyto tej zmiennej zamiast całej konstrukcji. Na przykład funkcja biblioteczna sqrt oczekuje argumentu typu double, będzie więc produkować nonsensowne wyniki, jeśli przypadkowo otrzyma coś innego. (Funkcję sqrt zdefiniowano w pliku nagłówkowym <math.h>.) Tak więc, jeżeli n jest liczbą całkowitą, możemy napisać
sqrt((double) n)
aby wartość n przekształcić do double przed przekazaniem jej do funkcji sqrt. Zapamiętaj, że rzut produkuje wartość argumentu n o odpowiednim typie; sam argument n nie ulega zmianie. Operator rzutowania ma ten sam priorytet, co inne operatory jed-noargumentowe (zajrzyj do tablicy na końcu tego rozdziału).
Jeśli argumenty zadeklarowano w prototypie funkcji, tak jak to należy robić prawidłowo, taka deklaracja powoduje automatyczne wymuszenie zmiany typu dla dowolnego argumentu, z którym funkcja została wywołana. Tak więc, przy danym prototypie funkcji
double sqrt(double);
jej wywołanie
root2 = sqrt(2); /* pierwiastek kwadratowy z 2 */
automatycznie wymusza zamianę całkowitej liczby 2 na zmiennopozycyjną wartość podwójnej precyzji 2.0, a zatem rzutowanie nie jest potrzebne.
Biblioteka standardowa zawiera przenośne wersje generatora liczb pseudo-losowych i funkcji inicjującej zarodek (ang. seed) dla ciągu takich liczb. Pierwsza z funkcji ilustruje zastosowanie rzutu:
73