Wyróżniamy kilka rodzajów operatorów. W zależności od sytuacji, w której mają zastosowanie dzielimy na kilka grup:
Dodatkowo w tym artykule przedstawię priorytety operatorów wykorzystywanych w C++.
Warto zauważyć, że operatory dzielimy także ze względu na ilość argumentów, jakie potrzebują do prawidłowego działania. Operatory jednoargumentowe takie jak "++" czy "--" nazywamy unarnymi np.:
int c = 0;
c++; //zauważmy, że do wykonania tej operacji potrzebny jest tylko jeden argument, w tym przypadku
//jest to zmienna o nazwie "c"
Operatory, które wykorzystują dwa argumenty nazywamy binarnymi np.:
int a = 9, b = 3;
int c = a + b;//operator + jest operatorem dwuargumentowym; argumentami są w tym przypadku zmienne "a" i "b"
Operatory arytmetyczne służą do wykonywania wszelkiego rodzaju działań na liczbach takich jak:
int a = 10, b = 3, wynik1;
wynik1 = a / b; //zmienna "wynik1" przechowa wartość 3, ponieważ 10 / 3 = 3 całe (dzielenie całkowite)
wynik1 = a % b; //zmienna "wynik1" w tym przypadku będzie miała nadaną wartość 1, ponieważ
// reszta z dzielenia liczby 10 przez 3 jest równa 1
wynik1 = b% a; //zmienna "wynik1" będzie równa 3, ponieważ tyle wynosi reszta z dzielenia liczby 3 przez 10
float c = 10, d = 3, wynik2;
wynik2 = c / d; //zmienna "wynik2" przechowa wartość 3.333..., ponieważ w tym przypadku zostało wykonane
//dzielenie całkowite
Mają zastosowanie w miejscach, gdzie występują różnego rodzaju warunki - głównie w pętlach i instrukcjach warunkowych. Do operatorów logicznych zaliczamy:
Lub logiczne zwraca prawdę, gdy przynajmniej jeden z warunków jest prawdziwy, w przeciwnym razie zwraca fałsz np.:
Załóżmy, że nasze zmienne przyjmują następujące wartości:
int n = 5; int k = 7;
bool warunek = n > 3 || k > 10; // wystarczy, aby jeden warunek był prawdziwy
// (w tym przypadku jest pierwszy) aby zmienna "warunek" otrzymała wartość true
Logiczne i zwraca prawdę w przypadku, gdy wszystkie warunki są prawdziwe, w przeciwnym razie zwraca fałsz np.:
int n = 5; int k = 7;
bool warunek = n > 3 && k > 10;// wystarczy, aby jeden warunek był fałszywy
// (w tym przypadku jest drugi) aby zmienna "warunek" otrzymała wartość false
Logiczne nie zaprzecza otrzymaną wartość z true na false lub z false na true np.:
int n = 5; int k = 7;
bool warunek = !(n > 3 && k > 10); // wyrażenie w nawiasie ma wartość false,
// a więc zmienna "warunek" będzie miała wartość true
Operatory relacyjne stosujemy w sytuacje, gdzie jest potrzeba porównania dwóch elementów. Najczęściej w instrukcjach warunkowych i iteracyjnych. Wyróżniamy:
Przykładowe zastosowanie:
int a = 8, b = 7;
if(a==b) //sprawdzam czy wartość zmiennej "a" jest taka sama jak zmienne "b"
cout<<"a jest równe b"; //ten komunikat się nie wyświetli, ponieważ "a" nie jest równe "b"
else
cout<<"a nie jest równe b";
Przypisanie polega na nadaniu wartości dla zmiennej znajdującej się po lewej stronie, wartości znajdującej się po stronie prawej. Dla takiej zmiennej można bezpośrednio nadać wartość, przekazać wartość z innej zmiennej lub wartość może zostać nadana poprzez wcześniejsze wykonanie pewnych operacji. Podstawowym operatorem przypisania jest "=".
int a = 45; //przypisanie do zmienne "a" liczby 45, od tej chwili zmienna "a" przechowuje tą liczbę
int b = a; //przypisanie do zmiennej "b" wartości zmiennej "a", od tej chwili zmienna "b" ma taką
//samą wartość co zmienna "a".
int c = b * (a - b); //w tym przypadku wykonane zostaną operacje po prawej stronie operatora
// a następnie ten wynik zostanie przypisany do zmiennej "c" (czyli 0)
Częstą operacją w języku C++ jest zwiększenie lub zmniejszenie wartości zmiennej całkowitej o 1. Tą operację nazywamy odpowiednio inkrementacją i dekrementacją. Przeanalizujmy przykład:
int c = 1;
c = c + 1; //zwiększenie wartości zmiennej "c" o 1, od tej pory ta zmienna ma wartość 2 - sposób pierwszy
c += 1; //ponowne zwiększenie zmiennej "c" o 1, zmienna ta przyjmuje wartość 3 - drugi sposób inkrementacji
c ++; // najkrótszy i najczęstszy sposób inkrementujący zmienną "c". Od tej pory zmienna ta osiąga wartość 4
c --; // i analogicznie dekrementujemy naszą zmienną, a więc zmniejszamy wartość zmiennej o 1.
Pamiętaj!!! Inkrementacja i dekrementacja działa tylko na zmiennych typu całkowitego.
Jak już wspomniałem, najczęściej używaną opcją jest inkrementacja i dekrementacja zastosowana w trzecim przykładzie. Ten rodzaj inkrementacji dzielimy na dwa rodzaje: przyrostkowa (c++; c--;) oraz przedrostkowa (++c; --c;). Na pierwszy rzut oka, efekt działania będzie taki sam. Różnica jednak jest w priorytecie przy zastosowaniu z operatorem przypisania. Prześledźmy przykład:
int a, b;
a = 1;
b = a ++; // w tym przypadku najpierw zostanie przypisana wartość do zmiennej "b",
// a następnie zwiększona wartość zmiennej "a" o 1, czyli od tej chwili
// " a = 2 " natomiast " b =1 "
a = 1;
b = ++a; // w tym przypadku najpierw zadziała operator inkrementacji, a później przypisania
// a więc, najpierw zwiększamy "a", a następnie ten wynik przypisujemy do "b",
//z tego wynika, że " a = 2 " i " b = 2 ".
Operację przypisania połączoną z pewną operacją matematyczną na wartości zmiennej można przedstawić w następujący sposób:
zmienna [operator matematyczny][=] wartość;
Aby lepiej zrozumieć powyższą notację prześledźmy przykłady:
int a = 3;
a += 4; //działanie to oznacza, że do aktualnej wartości zmiennej "a" dodajemy 4, czyli nowa wartość tej zmiennej wynosi 7
int a = 3;
a -= 4; //działanie to oznacza, że od aktualnej wartości zmiennej "a" odejmujemy 4, czyli nowa wartość tej zmiennej wynosi -1
int a = 3;
a %= 2; //działanie to oznacza, że pod zmienną "a" zostanie zapisany wynik reszty z dzielenie aktualnej wartości tej danej
// i 2, czyli 1
int a = 3;
a *= 4; //działanie to oznacza, że aktualna wartość zmiennej "a" zostanie pomnożona przez 4 i wynik zostanie nadpisany w tej zmiennej
//wynikiem tym jest liczba 12
Nie będę opisywał wszystkich przypadków tego rodzaju przypisania. Wypiszę tylko operatory, które można zastosować analogicznie do powyższych przykładów:
Operatory, o których mowa, wykonują operacje bezpośrednio na reprezentacji bitowej danej zmiennej.Załóżmy, że rozpatrujemy zmienną typu unsigned short, która zajmuje pole dwóch bajtów czyli 16 bitów. Teraz przypiszmy do tej zmiennej liczbę 19. Popatrzmy na tą liczbę w notacji binarnej:
19 = (00000000 00010011)2
Pierwszy operator bitowy to przesunięcie w prawo ">>". Przesuwa liczbę o n bitów w prawą stronę, gdzie n jest liczbą całkowitą, uzupełniając z lewej strony zerami:
unsigned short a = 19;
a>>=3; //przesunięcie bitów w zmiennej "a" o 3 w prawo, powstała w ten sposób liczba 2
Gdy przesuniemy liczbę 19 o trzy w prawo, skasują się trzy bity z prawej strony, natomiast z lewej strony dopełnią się zerami:
19 = (00000000 00010011)2
przesuwamy 3 bity w prawo i otrzymujemy
2 = (00000000 00000010)2
Analogicznym operatorem jest operator przesunięcia w lewo. Popatrzmy na przykład:
unsigned short a = 19;
a<<=2; //przesunięcie bitów w zmiennej "a" o 2 w lewo, powstała w ten sposób liczba 76
19 = (00000000 00010011)2
przesuwamy 2 bity w lewo i otrzymujemy
76 = (00000000 01001100)2.
Następnym operatorem bitowym jest koniunkcja bitowa - w notacji C++ "&". Zanim przejdziemy do przykładu poparzmy jak zachowują się bity potraktowane tym operatorem:
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
Zauważmy, że tylko w jednym przypadku otrzymamy jedynkę - gdy oba bity będą jedynkami. Rozpatrzmy już znaną nam liczbę 19 zapisaną pod typem unsigned short. Wykonajmy następującą operację:
unsigned short a = 19;
unsigned short b = a & 34; //otrzymaliśmy w ten sposób wartość 2, którą od tej pory przechowuje zmienna "b"
19 = | (00000000 00010011)2 | |
& | 34 = | (00000000 00100010)2 |
2 = | (00000000 00000010) 2 |
Podobnie działa alternatywa bitowa - w notacji C++ "|". W tym przypadku otrzymamy 0, gdy dwa bity są zerami, w przeciwnym wypadku będzie 1. Popatrzmy:
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
unsigned short a = 19;
unsigned short b = a | 34; //otrzymaliśmy w ten sposób wartość 51, którą od tej pory przechowuje zmienna "b"
19 = | (00000000 00010011)2 | |
| | 34 = | (00000000 00100010)2 |
51 = | (00000000 00110011) 2 |
Następnym ciekawym operatorem jest operator różnicy symetrycznej, wykorzystywany często w szyfrowaniu symetrycznym danych. W notacji C++ operator zapisujemy "^". Traktując dwa bity tym operatorem otrzymujemy 0, gdy te bity są takie same, 1 w przeciwnym razie:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
Rozpatrzmy przykład:
unsigned short a = 19;
unsigned short b = a ^ 34; //otrzymaliśmy w ten sposób wartość 49, którą od tej pory przechowuje zmienna "b"
19 = | (00000000 00010011)2 | |
^ | 34 = | (00000000 00100010)2 |
49 = | (00000000 00110001) 2 |
Ostatnim operatorem z tej serii jest jednoargumentowy operator negacji bitowej. W notacji C++ zapisujemy "~". Zasada działania jest prosta:
~ 0 = 1
~1 = 0.
Rozpatrzmy zmienną typu unsigned int znajdującą się na polu 4 bajtów, czyli 32 bitów. Przypiszmy wartość 19. Jaka będzie wartość tej zmienne po negacji? Zobaczmy:
unsigned int a = 19;
a = ~a; // w tym przypadku zmienna a po negacji przyjmie wartość 4294967276
19 = | (00000000 00000000 00000000 00010011)2 | |
~ 19 = | 4294967276 = | (11111111 11111111 11111111 11101100)2 |
W tym podrozdziale będziemy rozpatrywać kolejność wykonywanych operacji przez operatory. Przyjrzyjmy się następującemu działaniu matematycznemu:
343 · 5 - 6 = …
Wiadomo, że w tym przypadku kolejność jest następująca:
Najpierw potęgowanie, potem mnożenie, a na końcu odejmowanie. Aby zmienić kolejność działań możemy posłużyć się nawiasami np.:
343 · (5 - 6) = …
W tym przypadku zmieniliśmy priorytety wykonywanych działań. Najpierw zostanie wykonane działanie w nawiasie, następnie potęgowanie (lub na odwrót), potem dopiero mnożenie.
Tak samo dzieje się z operatorami. Jedne działają wcześniej (mają wyższy priorytet), inne później. Oto tabelka z operatorami i ich priorytetami.
Priorytet | Ilość arg. | Operator | Opis | Przykład |
---|---|---|---|---|
1 | 2 | :: | zakres, przestrzeń nazw | std::cout |
2 | 2 | () [] . -> ++ -- dynamic_cast static_cast reinterpret_cast const_cast typeid |
nawiasy, inkrementacja postfiksowa(przyrostkowa), odwołanie do metod obiektów, odwołania do pól struktur... |
i++, obiekt->metoda() |
3 | 1 | ++ -- ~ ! sizeof new delete * & + - |
inkrementacja przedrostkowa, prefiksowa, referencja, wskaźnik |
++i, +k, -k, & ref, * wsk |
4 | 1 | (typ) | rzutowanie | (double) a |
5 | 2 | .* ->* | ||
6 | 2 | * / % | mnożenie, dzielenie, modulo | a / b |
7 | 2 | + - | dodawanie, odejmowanie | a + b |
8 | 2 | << >> | przesunięcie bitów | a << 2 |
9 | 2 | < > <= >= | porównywanie | a < b |
10 | 2 | == != | porównywanie | a == b |
11 | 2 | & | bitowy iloczyn | |
12 | 2 | ^ | różnica symetryczna XOR | |
13 | 2 | | | bitowa suma | |
14 | 2 | && | iloczyn logiczny | (warunek1) && (warunek2) |
15 | 2 | || | suma logiczna | (warunek1) || (warunek2) |
16 | 3 | x ? y : z | operator warunkowy – zwraca y, gdy x ma wartość niezerową, z w przeciwnym wypadku | |
17 | 2 | = *= /= %= += -= >>= <<= &= ^= != |
przypisanie | a %= b |
18 | 2 | , | operator przecinkowy, służący np. do grupowania wyrażeń podczas inicjalizowania pętli | for(i = 1, j = 1;;i++,j--) |