172
ROZDZIAŁ 24. PRZENOŚNOŚĆ PROGRAMÓW
mie korzystamy z rozszerzenia naszego kompilatora. Rozważmy odwoływanie się do wartości wskazywanej przez wskaźnik o wartości NULL. Ponieważ według standardu operacja taka ma niezdefiniowany skutek to w szczególności może wywołać jakąś z góry określoną funkcję kompilator może coś takiego zrealizować sprawdzając wartość wskaźnika przed każdą dereft*-rencją. w ten sposób niezdefiniowane zachowanie dla konkretnego kompilatora stanie się jak naj liardziej zdefi n iowane.
Sytuacją wziętą z życia są operatory przesunięć bitowych, gdy działają na liczbach ze znakiem. Konkretnie przesuwanie w lewo liczb jest dla wielu przypadków niezdefiniowane. Bardzo często jednak, w dokumentacji kompilatora działanie przesunięć bitowych jest dokładnie opisane. Jest to o tyle interesujący fakt. iż wielu programistów nie zdaje sobie z niego sprawy i nieświadomie korzysta z rozszerzeń kompilatora.
Istnieje jeszcze trzecia klasa zachowań. Zachowania nieokreślone (ang. unsper.ifieA beha-tnour). Są to sytuacje, gdy standard określa kilka możliwych sposobów w jaki dane wyrażenie może działać i pozostawia kompilatorowi decyzję co z tym dalej zrobić. Coś takiego nie musi być nigdzie opisane w dokumentacji i znowu poleganie na konkretnym zachowaniu jest błędem. Klasycznym przykładem może być kolejność obliczania argumentów wywołania funkcji.
Rozmiar poszczególnych typów danych (np. char. int czy long) jest różna na różnych platformach. gdyż nie jest definiowany w sztywny sposób, jak np. “long int zawsze powinien mieć fil bity” (takie określenie wiązałoby się z wyżej opisanymi trudnościami), lecz w na zasadzie zależności typu "king powinien być nie krótszy niż int", "short nie powinien być dłuższy od int". Pierwsza standaryzacja języka C zakładała, że typ int będzie miał taki rozmiar, jak domyślna długość liczb całkowitych na danym komputerze, natomiast modyfikatory short oraz long zmieniały długość tego typu tylko wtedy, gdy dana maszyna obsługiwała typy o mniejszej lub większej długości1.
Z tego powodu, nigdy nie zakładaj, że dany typ będzie miał określony rozmiar. Jeżeli potrzebujesz typu o konkretnym rozmiarze (a dokładnej konkretnej liczbie bitów wartości) możesz skorzystać z pliku nagłówkowego stdint.h wprowadzonego do języka przez standard ISO C z 199!) roku. Definiuj©on typy intS.t. int lfi.t, int32.t. intfi-ł.t. uint&.t, uint lfi.t. uint32.t i uintfiJ.t (o ile w danej architekturze występują typy o konkretnej liczbie bitów).
Jednak możemy posiadać implementację, która nie p<*dada tego pliku nagłówkowego. W takiej sytuacji nie pozostaje nam nic innego jak tworzyć własny plik nagłówkowy, w którym za |K>mocą słówka typedef sami zdefiniujemy potrzebne nam typy. Np.:
typedef unsigned short ul6; typedef signed short sl6; typedef unsigned long u32;
typedef unsigned long long u64; typedef signed long long s64;
Aczkolwiek należy pamiętać, że taki plik Ijędzie trzeba pisać od nowa dla każdej architektury na jakiej chcemy kompilować nasz program.
‘Dokładniejszy opis rozmiarów dostępny jest w rozdziale Składnia.