Zaczynamy! Nowy rozdział o języku C# pojawił się na stronie. Zaglądać do niego, a dowiecie się jakie występują przeróżne operatory w języku C#. Zobaczcie co do czego...i dlaczego!

OPERATORY W JĘZYKU C# TO CAŁA "RODZINKA" ZNAKÓW!

Muszę znowu podzielić to na kategorie i wszyściusieńko dokładnie wytłumaczyć jak kilkunastoletniemu uczniowi. Bądźcie świadomi, że czeka Was kolejna dawka przydługiej treści o tamtym i owym. Przedstawiam Wam wszystkie "gatunki" operatorów jakie występują w "CSharpie".

ARYTMETYCZNE

Zacznijmy od arytmetycznych. Stare jak świat, takie jak dodawanie albo mnożenie służą właśnie do kalkulacji arytmetycznych. Z oczywistego powodu, mogą służyć jedynie liczbowym typom danych (całkowite i zmiennoprzecinkowe).

DODAWANIE

Ten sam "plus" i to samo zastosowanie. Sumowanie dwóch lub więcej składników, stąd też należy do operatorów binarnych lub też dwuargumentowych, gdyż wymaga operandów (argumentów liczbowych) po obu stronach znaku.

int i = 5 + 8;
float f = 14.56f + 18.68f;

Oprócz sumowania liczb, operator dodawania może też zostać zastosowany wobec łańcuchów znaków i wtedy "przemienia się" w konkatenację.

string s = "Ten tekst " + "stanie się " + "połączonym w całości.";
ODEJMOWANIE

Znak "minusa" to oczywiście odejmowanie lub różnica. Odjemna może być jedna, a odjemników wiele. Z tego samego powodu co wyżej, "minus" także należy do operatorów binarnych, choć może być również unarnym (jednoargumentowym), kiedy postawi się go przed liczbą.

int i = -5;
float f = 8.33f – 5.42f;
MNOŻENIE

Iloczyn wykonujemy dzięki znakowi "asterysku" (nazywanego zamiennie "gwiazdką"). To może być jedynie operator dwuargumentowy, gdyż zawsze trzeba coś przemnożyć przez siebie i naturalnie można go podstawić wyłącznie w kontekście liczb.

int i = 6*7;
float f = 14.5f*16.02f;
DZIELENIE

Ukośnik przeznaczony jest na iloraz. Nic się nie zmienia od pojawienia się języka C. Ze względu na przeznaczenie, także tutaj operator jest dostępny tylko wtedy, gdy występują operandy po przeciwnych stronach.

int i = 60 / 3;
float f = 10.56f / 8.07f;
RESZTA Z DZIELENIA

Tak zwany "operator modulo" to wyznaczanie reszty z dzielenia dwóch liczb całkowitych albo zmiennoprzecinkowych, a znakiem rozpoznawczym jest znak procent (%). Może być zastosowany wyłącznie w charakterze operatora dwuargumentowego.

int i = 70 % 8;
float f = 5.04f % 3.06f;
INKREMENTACJA

Arytmetyczne operatory w języku C# na tym się nie kończą. Gdy będziecie widzieć słowo "inkrementacja", to chodzi o zwiększenie wartości zmiennej liczbowej dokładnie o jedno "oczko" do góry. Czyli gdy zmienna posiada wartość 4, to po zastosowaniu inkrementacji będzie miała 5. O wiele ważniejsze informacje nadchodzą teraz. Występuje podział na dwa rodzaje: postinkrementacja i preinkrementacja, a w każdym z nich chodzi o kombinację obu "plusów" (++). To jest operator unarny.

POSTINKREMENTACJA

Postinkrementacja to inkrementacja "z opóźnieniem". Istotnie, ona podnosi wartość zmiennej o jeden do góry, ale dopiero po przekroczeniu pewnego momentu określanego jako "punkt sekwencyjny". To jest taki moment w programie, który jest uruchamiany kiedy wszystkie skutki uboczne (udane modyfikacje wartości i weryfikacje warunków) zostały zakończone w danym wierszu kodu. Ma to ogromne znaczenie szczególnie podczas działania w pętli, ponieważ to może decydować o poprawnym lub katastrofalnym działaniu programu. Wybór odpowiedniej inkrementacji wpływa na kolejność wykonywania i trzeba wówczas bardzo uważać którą się stosuje. Rozpoznacie ją automatycznie po dwóch "plusach" wstawionych PO nazwie zmiennej:

int i = 4;

i++;

Postinkrementacja powinna być używana tylko wtedy, kiedy nie da się inaczej zapisać zwiększenia wartości zmiennej tak, aby nie popsuć działania. Przykładem może być przypisywanie argumentom tablicy aktualnej wartości zmiennej, a jej wartość jest podnoszona co każdą iterację w pętli "for", ale dopiero po przypisaniu "starej" wartości:

int[] numbers = new int[100];
int counter = 1;

for (int i = 0; i < 100; i++)
{
	numbers[i] = counter++;
}

I już tłumaczę dlaczego postinkrementacja powinna być rzadziej wykorzystywana. Postinkrementacja to polecenie wydawane procesorowi, aby przez jakiś czas zachował kopię starej wartości zmiennej (i porzucił ją dopiero po przekroczeniu wyżej opisanego punktu sekwencyjnego), przez co pojawiło się mnóstwo spekulacji o tym, że przez to program "chodzi" wolniej. Nie wiem ile jest w tym prawdy, niemniej jednak chciałem żebyście wiedzieli że taka teoria istnieje.

W pętlach "for" będziecie to widzieć co najmniej bardzo często. Ja nie korzystam w swojej praktyce z postinkrementacji w pętli "for", tylko z preinkrementacji do której przejdziemy.

PREINKREMENTACJA

Preinkrementacja to także zwiększenie wartości zmiennej o "oczko" do góry, ale tutaj to podniesienie następuje natychmiast. Nie ma też żadnego przechowywania kopii starej wartości, tutaj CPU podnosi liczbę i koniec tematu. Żeby nie było spekulacji, to także wpływa na przebiegi pętli takich czy śmakich. Tym razem dwa plusiki ląduje na początek czyli PRZED nazwą zmiennej!

int i = 4;

++i;

Gdyby ktoś się zastanawiał jak można to podstawić do przykładu z tablicą, to załączam delikatnie zmodyfikowaną próbkę:

int[] numbers = new int[100];
int counter = 0;

for (int i = 0; i < 100; i++)
{
	numbers[i] = ++counter;
}

Spróbujcie sami pomyśleć czemu licznik musi być wycofany o jednostkę do tyłu, żeby to działało tak samo.

DEKREMENTACJA

Dekrementacja to taka sama bajka co inkrementacja, tylko tutaj chodzi o redukcję wartości o jeden, a nie o podnoszenie. Naturalnie jest taki sam podział na "predekrementację" oraz "postdekrementację", oba mają taki sam mechanizm i tak samo trzeba uważać na różnice podczas funkcjonowania w pętli.

RELACYJNE

Relacyjne operatory w języku C# służą do konstruowania zdań logicznych dla instrukcji warunkowych oraz logicznego typu danych "bool". Posłużę się teraz wykazem tabelarycznym, aby zaprezentować Wam każdy z nich i pokrótce opisać.

Operator Znaczenie
== równe
!= różne od
> większe od
>= większe bądź równe od
< mniejsze od
<= mniejsze bądź równe od

Możecie porównywać ze sobą liczby, łańcuchy, nawet obiekty (pod pewnymi warunkami). Macie prawo do podstawienia każdego typu, o ile program będzie w stanie kierować się kryteriami mającymi na celu wytypowanie wyniku ostatecznego (innymi słowy, będzie się dało porównać jedno z drugim).

Niemal w każdej książce, artykule bądź innej treści na temat operatorów relacyjnych w programowaniu, będzie napisane wielkimi wołami żeby nie mylić operatora przypisania (=) z operatorem porównującym (==). I tak, ja o tym też wspomnę, bo w tym przypadku można aż za bardzo iść śladami matematyki. Kiedy porównujemy dwie wartości, to stawiamy pomiędzy nimi DWA znaki równości!

LOGICZNE

Operatory w języku C# to także te "oflagowane" jako logiczne. Efektem ich działania jest zwrócenie wartości logicznej "true" (prawda) albo "false" (fałsz) przez sformułowane warunki i uzyskane w ten sposób zdanie (w rozumieniu logiki). Posługując się operatorami relacyjnymi, konstruujemy w ten sposób zdania. Istnieje kilka wariantów operatorów logicznych i każdy z nich oferuje co innego.

KONIUNKCJA

Koniunkcja, czyli znany ze szkoły AND. Zdanie logiczne zwracające prawdę wyłącznie gdy wszystkie warunki są spełnione. Znak reprezentujący koniunkcję to para "ampersandów" (&&), a operator oczywiście oczekiwać będzie dwóch operandów po obu stronach.

bool b = (5 > 2 && (6 / 2) == 3);	// prawda, bo oba warunki spełnione
ALTERNATYWA

OR to przedstawiciel alternatywy, czyli zdania zwracającego prawdę kiedy którykolwiek z warunków jest prawdziwy. Dwie pionowe kreski kryjące się pod klawiszem obok Entera przeznaczone są dla alternatywy. I tu także wstawiamy oba operandy, jeden po lewej, jeden po prawej.

bool b = (7 % 2 == 5 || 5*5 == 25);	// prawda, bo jeden z warunków jest spełniony
NEGACJA

Negacja to NOT, odwrócenie wyniku zdania logicznego. Jak było prawdą, staje się fałszem i odwrotnie. Wykrzyknik, tym znakiem stosujecie negację i jak widzicie, jest to operator unarny.

bool b = !(7 == 7);	// fałsz, inwersja prawdy
ALTERNATYWA WYKLUCZAJĄCA

Kojarzycie XOR? To się też nazywa "alternatywa wykluczająca". Zdanie logiczne zwracające prawdę kiedy nieparzysta liczba warunków jest prawdziwa. Nieparzysta, podkreślam! Za pomocą takiego "dzióbka" (^) tworzycie alternatywę wykluczającą (nazywaną też rozłączną).

bool b = (2 == 3 ^ 5 == 5 ^ 6 == 7);	// prawda, bo tylko jeden z trzech warunków jest prawdziwy

BITOWE

Bitowe operatory w języku C# są bardzo podobne "znakowo" do operatorów relacyjnych, choć ich znaczenie jest już całkowicie odmienne. Ponieważ tutaj odgrywa się zabawa kręcąca się wokół bitów, dozwolone jest wstawianie tylko liczb całkowitych. Obejrzyjmy je co one dają.

ILOCZYN BITOWY

Iloczyn bitowy identyfikowany jest także terminem "koniunkcja bitowa". Za pomocą pojedynczego znaku "ampersandu" (&) wyznaczamy liczbę całkowitą uzyskaną w wyniku zastosowania iloczynu bitowego. Po szczególne kroki postępowania co się wtedy dzieje, skieruję Was do osobnego artykułu bo tu za dużo tłumaczenia jak na ten temat. Ze względu na naturę i zwracaną wartość, jest to operator oczywiście binarny (dwuargumentowy).

int i = 5 & 6;	// 4
ALTERNATYWA BITOWA

Alternatywa bitowa (albo zamiennie "suma bitowa") utożsamiana z pojedynczą pionową kreską (|) to liczba całkowita uzyskana po obliczeniu sumy bitowej. Kieruję do tego samego artykułu po szczegóły w jaki sposób jest to obliczane. Jak poprzednik, ten operator także nie obejdzie się bez dwóch operandów.

int i = 5 | 6;		// 7
ALTERNATYWA WYKLUCZAJĄCA

XOR występuje również w wersji zwracającej liczbę całkowitą, która jest kalkulacją alternatywy rozłącznej z podanych liczb. Po raz trzeci, operator dwuargumentowy.

int i = 5 ^ 6;	// 3
NEGACJA BITOWA

NOT dla bitów? Absolutnie możliwe! Znak tyldy (~) przestawia dosłownie wartości bitów na przeciwne. Tam, gdzie było 0, tam będzie 1 i na odwrót.

int i = ~6;	// -7

Uważajcie na działanie negacji dla typów "ze znakiem" (ang. "signed")! Dla tego rodzaju liczb stosuje się kod uzupełnień do dwóch. W dużym skrócie - w tym systemie ostatni bit wysunięty na lewo staje się "bitem znaku" który decyduje o tym, czy liczba jest dodatnia, czy ujemna. Z tego powodu zanegowanie "szóstki" zwróci wynik -7, ponieważ po skonwertowaniu liczby na system binarny, zauważymy że zerowe bity występują po obu końcach:

6(dec) = 0110(bin)

Stąd też zanegowanie wszystkich bitów da taki efekt:

0110
==== NOT
1001

ale to jest "int" ze znakiem! Dlatego też pierwszy bit wysunięty na lewo jest bitem znaku, a ponieważ tam jest jedynka, to spowoduje to zamianę na liczbę ujemną (będzie -8). Po zsumowaniu wszystkich składników:

1001(bin) = -2^3 + 2^1 = -8 + 1 = -7(U2)

otrzymujemy w ten sposób -7. Gdyby ktoś się dziwił czemu kompilator może zgłosić błąd podczas próby przypisania niektórych kombinacji dla typów bez znaku ("unsigned"), przypominam o zasadzie "przewidywania" wyjścia wyniku poza zakres podanego typu podczas inicjalizacji. Jeśli kompilator "zauważy" na etapie przypisywania, że liczba ewidentnie wychodzi poza zakres, to nie pozwoli na uruchomienie aplikacji. Powodem jest przekroczenie zakresu liczb całkowitych bez znaku (-7 < 0).

PRZESUNIĘCIE BITOWE

Ostatnie bitowe operatory w języku C# są przesunięcia bitowe. Operatory przesunięć bitowych również "grzebią" w bitach liczb całkowitych i ich celem jest przekształcenie aktualnej wartości na inną. W zależności od tego, czy przesunięcie odbywa się w lewą lub prawą stronę, uzyskujemy inny efekt.

PRZESUNIĘCIE W LEWO

Przesunięcie bitów w lewo to de facto mnożenie przez N-tą potęgę liczby 2. Jeśli rozłożycie sobie liczbę dziesiętną na binarny odpowiednik i dosłownie przesuniecie cały ciąg zer i jedynek o N bitów, to po zamianie wyniku na system dziesiętny, uzyskacie literalny iloczyn.

int i = 5 << 2;	// 5*4 = 20
PRZESUNIĘCIE W PRAWO

Gdy mowa o przesuwaniu bitów w prawo to mamy odwrotną wręcz operację, czyli dzielenie przez N-tą potęgę liczby 2. Tak samo, jeśli zamienimy sobie liczbę dziesiętną na binarną i przesuniemy wszystkie bity w prawo, liczba stanie się mniejsza przez co uzyskamy efekt identyczny jak po wstawieniu znaku ilorazu i dzielnika w postaci 2N. Miejcie się na baczności! W przypadku, gdy zostanie Wam jakiś ułamek, to zostanie on obcięty, nie zaokrąglony!

int i = 5 >> 2;	// 5/4 = 1

PRZYPISANIA

Do operatorów w języku C# zaliczymy także operatory przypisania, kończąc w ten sposób wymienianie rodzajów. Zaprezentuję wszystko jeszcze raz przy użyciu tabelki, żeby nie przedłużać.

Operator Zapis alternatywny
Znaczenie
= brak przypisanie
+= a = a + N
dodawanie do wartości
-= a = a - N odejmowanie od wartości
*= a = a*N mnożenie przez wartość
/= a = a / N dzielenie przez wartość
%= a = a % N przypisanie reszty z dzielenia
&= a = a & N przypisanie wyniku iloczynu bitowego
|= a = a | N przypisanie wyniku alternatywy bitowej
^= a = a ^ N przypisanie wyniku bitowej alternatywy rozłącznej
<<= a = a << N przesunięcie bitowe w lewo wartości o N bitów (mnożenie przez 2N)
>>= a = a >> N przesunięcie bitowe w prawo wartości o N bitów (dzielenie przez 2N)

Jak widzicie, zdecydowana większość operatorów to są skróty instrukcji przypisania z operatorem określonego rodzaju.

Operatory w języku C#

Istnieje wiele operatorów przydzielonych do jednej z kilku kategorii, które służą do operacji arytmetycznych, logicznych, relacyjnych lub bitowych.


Dotarliśmy w końcu do mety. Operatory w języku C# to temat bardziej rozległy niż trudny, bo szczegóły zaczynają się odkrywać dopiero po wnikliwej analizie. Gdy stopniowo zaczniecie z nich korzystać częściej, wtedy będziecie w stanie nawet już w samej głowie układać instrukcje z ich wykorzystaniem. Uwierzcie mi, po pewnej praktyce to się "włącza".

PODOBNE ARTYKUŁY