Operatory bitowe zostały wprowadzone do języka C dla wygody niskopoziomowej pracy z danymi i sprzętem: konfiguracja rejestru, maskowanie, mnożenie i dzielenie przez potęgę liczby dwa. Zasady ich działania ukształtowały się już w czasach procesorów 8- i 16-bitowych.
Często popełniane są błędy przy przesunięciach typów signed, ponieważ wynik zależy od implementacji (przesunięcie arytmetyczne lub logiczne) oraz przy przekroczeniu granic rozmiaru typu. Błędy prowadzą do uszkodzenia danych, błędnych obliczeń, nieokreślonego zachowania.
Przesunięcie w lewo (<<): jest równoważne mnożeniu wartości przez 2 do potęgi k (a << k). Zawsze wypełnia zera z prawej strony.
Przesunięcie w prawo (>>): dla wartości unsigned wypełnia z lewej strony zerami (przesunięcie logiczne), a dla signed — może wypełniać zarówno bitem znaku (przesunięcie arytmetyczne), jak i zerami (zachowanie zależy od kompilatora).
Przykład:
unsigned int x = 5; // 0000 0101 unsigned int y = x << 1; // 0000 1010 == 10 int z = -4; // 1111 1100 (jeśli 8 bitów) int w = z >> 1; // Może zostać 1111 1110 (-2) lub 0111 1110 (zależy od implementacji)
Kluczowe cechy:
Co się stanie przy przesunięciu ujemnej liczby w prawo przez >>?
Wynik zależy od implementacji: najczęściej jest to przesunięcie arytmetyczne z zachowaniem znaku, ale standard tego nie gwarantuje!
Jaką wartość ma wynik przesunięcia o liczbę bitów większą niż rozdzielczość typu?
Niezdefiniowane zachowanie. Na przykład 1 << 32 dla typu 32-bitowego może dać cokolwiek lub nawet zakłócić działanie programu.
Czy można używać operatorów bitowych dla liczb zmiennoprzecinkowych?
Nie, standardowe typy float, double nie obsługują operacji bitowych. Tylko typy całkowite.
Programista przesuwał int o 32, aby utworzyć maskę — na niektórych platformach prowadziło to do zera, na innych — do niepoznawalnej wartości.
Zalety:
Wady:
Zamiast tego używano wartości unsigned i maskowania liczby bitów z użyciem makropoleceń, z jasno określoną dokumentacją i sprawdzaniem długości typu za pomocą sizeof.
Zalety:
Wady: