Programowanie i algorytmy

Operatory

 

powrót

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

Operatory arytmetyczne służą do wykonywania wszelkiego rodzaju działań na liczbach takich jak:

  • "+" - dodawanie
  • "-" - odejmowanie
  • "*" - mnożenie
  • "/" - dzielenie całkowite lub rzeczywiste. (jeśli argumentami są liczby całkowite, operator będzie wykonywał dzielenie całkowite, natomiast dla liczb rzeczywistych operator wykona dzielenie rzeczywiste. (przykład poniżej)
  • "%" - reszta z dzielenia dwóch liczb całkowitych

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

Operatory logiczne

 

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 (klikamy dwa razy shift + backslash)
  • && - i logiczne (klikamy dwa razy shift + 7)
  • ! - zaprzeczenie (klikamy wykrzyknik)

 

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

 

Operatory relacyjne stosujemy w sytuacje, gdzie jest potrzeba porównania dwóch elementów. Najczęściej w instrukcjach warunkowych i iteracyjnych. Wyróżniamy:

 

  • "<" - mniejszy
  • ">" - większy
  • "<=" - mniejszy równy
  • ">=" - większy równy
  • "==" - równy
  • "!=" - różny

 

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";

 

 

 

Operatory przypisania

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:

 

  • "a /= 2;" - podzielenie aktualnej wartości zmiennej "a" przez 2 i nadpisanie tym wynikiem tą zmienną
  • "a >>= 2;" - przesunięcie o 2 bity w prawo
  • "a <<= 2;" - przesunięcie o 2 bity w lewo
  • "a &= 2;" - równoważne z operacją "a = a & 2" - operatory logiczne w następnym podrozdziale
  • "a |= 2;" - równoważne z operacją "a = a | 2" - operatory logiczne w następnym podrozdziale
  • "a ^= 2;" - równoważne z operacją "a = a ^ 2" - operatory logiczne w następnym podrozdziale

Operatory bitowe

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

Priorytety operatorów

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.

 

 

PriorytetIlość arg.OperatorOpisPrzykł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 2 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--)