
W tej lekcji powiemy sobie o operacjach binarnych czyli bezpośrednio na bitach w bajcie. My patrząc na jakąś zmienną, np. na "int", patrzymy na nią jak na jakąś liczbę np. 123, ale w rzeczywistości jest to układ zer i jedynek. Każda zmienna to zera i jedynki i musimy mieć świadomość, że nie musimy traktować int'a jak liczby, ale jak 32 kolejne bity, w których możemy zapisać dwie wartości 0 lub 1. W tej lekcji pokażę jak sprawdzać i zmieniać pojedyncze bity w zmiennej.
Do manipulowania bitami używa się masek i odpowiednich operatorów. Zajmijmy się maską, jest to wartość, która pomoże nam osiągnąć zamierzony rezultat operacji binarnej, ciężko to wytłumaczyć, dlatego zastosowanie masek zobaczymy w konkretnych zastosowaniach.
Dobrze więc, przejdźmy do pierwszego przykładu. Mamy liczbę int(32 bitową) powiedzmy, że jej wartość poda użytkownik i chcemy sprawdzić w niej 20 bit. Do tej operacji będzie potrzebny nam operator & (iloczyn bitowy, ma ten sam znak co operator wyłuskania adresu, ale to inny operator), który porównuje wszystkie bity dwóch zmiennych i jeżeli bity na tych samych miejscach będą wynosiły po 1, to w wyniku będzie także 1, jeżeli nie w wyniku będzie bit 0. Spójrzmy na schemat:
Jak widzisz działanie tego operatora nie jest zbyt skomplikowane. Teraz wróćmy do naszego przykładu mamy w jakiejś liczbie(32 bitowej) sprawdzić 20 bit. Już wiadomo, że trzeba użyć operatora iloczynu bitowego &, jeszcze potrzeba nam maski. Jak już wcześniej powiedziałem maska to zmienna, której używamy, aby uzyskać dany rezultat, w naszym przypadku chcemy sprawdzić 20 bit, wszystkie inne bity są dla nas nie istotne, dlatego nasza maska na wszystkich bitach oprócz dwudziestego powinna mieć bity 0, a istotny jest dla nas bit 20, dlatego tam powinna mieć 1, czyli nasza maska do tej operacji powinna wyglądać tak:
Pamiętajmy, że bity liczymy od prawej strony, czyli ten najbardziej z prawej jest pierwszy. A ten z lewej ostatni, czyli 32 bit.
Teraz przeprowadźmy naszą operację sprawdzenia bitu w jakiejś liczbie:
Jak widać, zawsze będą możliwe tylko dwa wyniki tej operacji, jeżeli bit 20 będzie zerowy, to wynikowa zmienna będzie miała wszystkie bity równe 0(wartość 0). Jeżeli 20 bit będzie nie zerowy(1), wtedy, zmienna wynikowa, będzie miała 20 bit równy 1, więc będzie równa naszej masce. W ten sposób możemy sprawdzić wartość jakiegoś bitu, oczywiście, aby sprawdzić wartość innego bitu, trzeba zmienić maskę, w której musimy podać sprawdzany bit jako 1, a resztę jako 0. Możliwe są sprawdzania większej ilości bitów, ale już dla dwóch sprawdzanych bitów, możliwe będą 4 wartości, dla 3 bitów, 9 możliwych wartości.
No to przejdźmy do kodzenia w C++. My nie będziemy bezpośrednio wypisywać bitów, ale będziemy podawać wartości liczbowe tych konfiguracji bitów. Teraz skąd mamy wiedzieć, jaka to liczba, która ma 20 miejscu bit 1, a reszta to bity 0. Z pomocą przychodzi nam windowsowski kalkulator, w którym możemy sobie skonwertować liczby z kodu binarnego do szesnastkowego czy dziesiątkowego.
Zatem potrafimy już sprawdzać poszczególne bity w zmiennej, za pomocą maski. Tylko po co nam taka umiejętność? Otóż, dzięki temu możemy uczynić nasze programy bardziej oszczędnymi. Możemy zapisywać poszczególne dane jako pojedyncze bity w jednej zmiennej. Skoro możemy już odczytywać bity, przydałoby się móc je ustawiać. Do tego także przydadzą nam się maski i operator sumy bitowej.
No więc może najpierw poznajmy zasadę działania operatora sumy bitowej |. Ten operator w zmiennej wynikowej ustawia wartość bitu 1, jeżeli chociaż jedna z wartości bitu w obu zmiennych wynosi 1.
Teraz maska, więc tutaj jest tak samo, powiedzmy, że chcemy ustawić piąty bit na 1, w tym celu przygotowujemy maskę, w której piąty bit ma wartości 1, a reszta 0, przy czym łączymy te zmienne operatorem sumy bitowej. Wygląda to tak:
Tym sposobem, możemy ustawić każdy bit na 1, nic nie stoi na przeszkodzie, aby w masce ustawić kilka bitów na 1, dzięki czemu od razu kilka bitów zostanie ustawione na 1. Tutaj dla ścisłości powiem, że jeżeli wcześniej ustawiany bit miał wartość 1, to się po prostu nic nie stanie, zmienna wynikowa będzie miała taką samą postać jak ustawiana zmienna.
W Windows API często używa się masek, nazywa się je także flagami. Często będziemy ustawiać różne parametry, łącząc ze sobą różne flagi, musimy mieć świadomość, że w ten sposób ustawiamy poszczególne bity w zmiennej, każdy bit może być inną opcją. Nie ma problemu, aby w swoim programie także, zrobić zmienną, która będzie zawierała konfigurację bitów, a każdy bit będzie oznaczał, że coś jest włączone lub nie.
Przykładowo, chcemy mieć w programie 5 opcji które, będziemy ustawiać w zmiennej "opcje". Pierwszy bit, będzie odpowiedzialny za wyświetlenie uśmieszku, drugi języka, trzeci procentów ;), czwarty zaś mówi nam czy ma być wyświetlone ^^, ostatni piąty bit będzie wyświetlał jakiś szlaczek. Sprawa prosta, jeżeli dany bit będzie wynosił 0 opcja będzie wyłączona, jeżeli 1 włączona. Do pięciu opcji wystarczy nam 5 bitów, więc musimy użyć zmiennej nie większej niż 8 bitów, czyli w C++ "unsigned char".