i dodanie liczby całkowitej z 0xFF
pozostawia tylko najmniej znaczący bajt. Na przykład, aby uzyskać pierwszy bajt w short s
, możesz napisać s & 0xFF
. Jest to zwykle określane jako „maskowanie”. Jeśli byte1
jest typem pojedynczego bajtu (jak uint8_t
) lub jest już mniejszy niż 256 (i w rezultacie wszystkie zera z wyjątkiem najmniej znaczącego bajtu), nie ma potrzeby maskowania wyższych bitów, ponieważ są one już zerowe.
Zobacz odpowiedź tristopiapatricka Schlütera poniżej, kiedy możesz pracować z podpisanymi typami. Podczas wykonywania operacji bitowych zalecam pracę tylko z niepodpisanymi typami.
jeśli byte1
jest 8-bitowym typem liczby całkowitej, to nie ma sensu – jeśli jest więcej niż 8 bitów, zasadniczo da ci ostatnie 8 bitów wartości:
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 & 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 ------------------------------- 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1
niebezpieczeństwo drugiego wyrażenia pojawia się, gdy typ byte1
jest char
. W takim przypadku niektóre implementacje mogą mieć wartość signed char
, co spowoduje rozszerzenie znaku podczas ewaluacji.
signed char byte1 = 0x80;signed char byte2 = 0x10;unsigned short value1 = ((byte2 << 8) | (byte1 & 0xFF));unsigned short value2 = ((byte2 << 8) | byte1);printf("value1=%hu %hx\n", value1, value1);printf("value2=%hu %hx\n", value2, value2);
wydrukuje
value1=4224 1080 rightvalue2=65408 ff80 wrong!!
próbowałem go na GCC v3.4.6 na Solarisie SPARC 64 bit i wynik jest taki sam z byte1
i byte2
zadeklarowanych jako char
.
TL;DR
maskowanie ma na celu uniknięcie domyślnego rozszerzenia znaku.
EDIT: sprawdziłem, to samo zachowanie w C++.
EDIT2: zgodnie z żądaniem wyjaśnienia rozszerzenia znaku.Rozszerzenie znaku jest konsekwencją sposobu, w jaki C ocenia wyrażenia. W C istnieje reguła zwana regułą awansu. C domyślnie rzuci wszystkie małe typy na int
przed wykonaniem oceny. Zobaczmy, co stanie się z naszą ekspresją:
unsigned short value2 = ((byte2 << 8) | byte1);
byte1
jest zmienną zawierającą wzór bitowy 0xFF. Jeśli char
jest unsigned
to wartość jest interpretowana jako 255, jeśli signed
to -128. Podczas wykonywania obliczeń, C rozszerzy wartość do rozmiaru int
(ogólnie 16 lub 32 bitów). Oznacza to, że jeśli zmienna jest unsigned
i zachowamy wartość 255, to wzór bitowy tej wartości jako int
będzie równy 0x000000FF. Jeśli jest to signed
chcemy mieć wartość -128, której bitowy wzorzec to 0xFFFFFFFF. Znak został rozszerzony do rozmiarów tempory używanej do obliczeń.I w ten sposób Oring tymczasowy przyniesie zły wynik.
przy montażu x86 robi się to za pomocą instrukcji movsx
(movzx
dla rozszerzenia zerowego). Inne procesory miały inne instrukcje (6809 miał SEX
).