8. Operacje logiczne
O ile w przypadku operacji artymetycznych wynikiem mogła być dowolna liczba (oczywiście z określonego zakresu), to wynikiem operacji logicznej jest jeden z dwóch możliwych stanów – prawda lub fałsz . W języku C za fałsz uznaje się liczbę zero, natomiast wszystkie pozostałe są uznawane za prawdę . Wynikiem takiego traktowania stanów logicznych jest możliwość używania kompaktowych wersji porównań logicznych, co zobaczymy w następnej części tego punktu.
8.1. Porównania
W tym podpunkcie przedstawię je tylko, natomiast przykłady ich wykorzystania zostaną zaprezentowane w następnym podpunkcie. W języku C mamy następujące operatory porównania:
C
Pascal
Opis
>
>
większe niż
<
<
mniejsze niż
>=
>=
większe lub równe
<=
<=
mniejsze lub równe
==
=
równe (zwróć uwagę na podwójny znak równości)
!=
<>
nie równe
8.2. Instrukcja if
Instrukcja ta jest istotnym elementem każdego programu, ponieważ pozwala ona na modyfikację sposobu, w jaki działa program w zależności od wartości danych. W języku C, podobnie jak w Pascalu, ma ona następującą składnię:
if(wyrażenie) wyrażenie1 else wyrażenie2
Jeśli wyrażenie w nawiasie ma wartość logiczną prawda to zostanie wykonane wyrażenie1, w przeciwnym wypadku zostanie wykonane wyrażenie2. Przykładowo:
if( a > 10 ) printf(„Zmienna a jest większa od dziesięciu !”); else printf(„Zmienna a jest mniejsza lub równa dziesięć !”);
Zauważ, że zarówno wyrażenie po if , jak i wyrażenie po else jest zakończone średnikiem. Uwagę tą kieruję szczególnie do osób znających Pascala, w którym średnik stawia dopiero na samym końcu. Oczywiście, tak jak w Pascalu, części „else” możesz w ogóle nie używać, jeśli nie jest Ci to akurat potrzebne. Czyli możesz napisać tak:
if( a > 10 ) printf(„Zmienna a jest większa od dziesięciu !”);
Jeśli chcesz wykonać kilka instrukcji jeśli spełniony jest pewien warunek to musisz je zawrzeć w bloku ograniczonym znakami { i } . Czyli wyglądałoby to następująco:
if( a > 10 ) {
printf(„Zmienna a jest wieksza od dziesięciu !
„);
printf(„Jest bowiem równa %d.”, a);
} else {
printf(„Zmienna a jest mniejsza lub równa dziesięć !
„);
if(a != 5) printf(„Jednak nie jest równa pieć !”);
}
Oczywiście to, jak sformatujesz ten tekst (np. możesz znak rozpoczynający blok wpisań w nowej linii) zależy tylko od Ciebie. Ja jednak preferuję taki sposób, według mnie jest to bardziej czytelne.
Pozostała do omówienia jeszcze jedna rzecz dotycząca instrukcji if , a związana z traktowaniem przez język C wartości logicznych. Zamiast:
if ( a != 0 ) printf(„a jest rozne od zera”);
możemy napisać:
if( a ) printf(„a jest rozne od zera”);
i będzie to działało w identyczny sposób. Jak myślisz dlaczego ? Jeśli przeczytałeś uważnie wstęp do operacji logicznych to nie powinieneś mieć większych problemów z odpowiedzią na to pytanie. W pierwszym przypadku mamy do czynienia z porównaniem wartości zmiennej a do zera. Przykładowo, jeśli zmienna a jest równa dziesięć to warunek „a != 0” zwróci prawdę do instrukcji if i zostanie wyświetlony na ekranie tekst. Natomiast co z drugim przypadkiem ? Nie mamy tu do czynienia z żadnym porównaniem, do instrukcji if jest od razu przekazywana wartość zmiennej a, czyli w naszym przypadku dziesięć. Zauważ, nie prawda , nie fałsz , ale liczba dziesięć ! I co się stanie teraz ? Po prostu na ekranie pojawi się tekst. Pamiętasz jak język C traktuje wartości logiczne ? Stanowi fałsz odpowiada liczba zero, natomiast stanowi prawda każda inna wartość. Czyli także liczba dziesięć ! Tak więc liczba ta zostanie potraktowana jako stan prawda i w wyniku tego zostanie wykonana odpowiednia instrukcja (blok instrukcji) – w naszym przypadku zostanie wywołana funkcja printf .
Na zakończenie tego punktu zamieściłem jeszcze przykład często popełnianego błędu (który na dodatek bardzo trudno jest zlokalizować), związanego z właśnie takim traktowaniem stanów logicznych przez język C:
if( a = 0 ) printf(„a jest równe zero”);
else printf(„a jest różne od zera”);
Jak myślisz, co zostanie wyświetlone na ekranie, jeśli powiem Ci, że zmienna a jest równa zero ? Jeśli odpowiesz, że będzie to „a jest równe zero” to niestety nie będziesz miał racji. Możesz zapytać: „Ale dlaczego ? Przecież mamy porównanie a do zera i ponieważ a jest równe zero, to powinien wyświetlić się pierwszy napis.”. Miałbyś rację, jeśli naprawdę byłoby tam porównanie. Jednak przyjrzyj się uważnie – tam nie ma porównania ! Porównanie w języku C to podwójny znak równości, natomiast pojedynczy (tak jak jest w tym przypadku) oznacza przypisanie. Tak więc najpierw zostanie tu przypisana wartość zero do zmiennej a, a następnie (tak jak w poprzednim przykładzie) wartość tej zmiennej zostanie przekazana do instrukcji if , która potraktuje ją jako fałsz (bo zero właśnie to oznacza) i w efekcie wywoła funkcję printf występującą po else .
8.3. Podstawowe operacje logiczne
W podpunkcie tym przedstawię cztery podstawowe operacje logiczne – OR, AND, NOT i XOR. Jeśli wiesz na jakiej zasadzie one działają to możesz przejść do następnego podpunktu. Zero w tabeli odpowiada stanowi fałsz , natomiast jedynka odpowiada stanowi prawda .
Operacja OR (lub)
Wejście
Wyjście
0
0
0
1
0
1
0
1
1
1
1
1
Wynikiem operacji OR jest zero, gdy oba argumenty są równe zero, lub jedynka w przeciwnym wypadku.
Operacja AND (i)
Wejście
Wyjście
0
0
0
1
0
0
0
1
0
1
1
1
Wynikiem operacji AND jest jedynka, gdy oba argumenty są równe jeden, lub zero w przeciwnym wypadku.
Operacja XOR
Wejście
Wyjście
0
0
0
1
0
1
0
1
1
1
1
0
Wynikiem operacji XOR jest jedynka, gdy tylko jeden z argumentów jest równy jeden, lub zero w przeciwnym wypadku.
Operacja NOT
Wejście
Wyjście
0
1
1
0
Wynikiem operacji NOT jest jedynka, gdy argument był równy zero, lub zero, gdy argument był równy jeden.
8.4. Operacje logiczne w języku C
Przedstawione w poprzednim punkcie operacje mają w języku C dwa aspekty – logiczny i arytmetyczny (operacje na bitach). Co prawda drugi z nich należałoby przedstawić już wcześniej, przy okazji omawiania operacji arytmetycznych, jednak zrobię to dopiero w tym punkcie z uwagi na podobieństwo. Najpierw jednak skupmy się na pierwszym aspekcie. Oto jak operacje logiczne zapisujemy w języku C:
Operacja
C
Pascal
OR
||
or
AND
&&
and
NOT
!
not
Ponieważ o wiele łatwiej jest uczyć się na przykładach, podam teraz program, który zilustruje wykorzystanie poznanych operacji logicznych.
#include <stdio.h>
void main(void)
{
int a = 17;
// przykład użycia operacji logicznej OR
if( (a 10) ) {
printf(„Zmienna a jest mniejsza od pięciu _lub_ większa od dziesięciu.
„);
}
// przyklad użycia operacji logicznej AND
if( (a 10) && (a
Ponieważ początek programu nie wymaga chyba komentarza (jeśli wymaga to cofnij się, proszę, do poprzednich punktów), analizę naszego programu zaczniemy od linijki z pierwszym wystąpieniem instrukcji if .
W naszym przypadku chcemy wykonać jakieś działanie (wyświetlenie tekstu na ekranie), gdy zmienna a jest mniejsza od pięciu lub też większa od dziesięciu. Idealnie do tego celu nadaje się operacja logiczna OR , której używa się, gdy chcemy sprawdzić czy chociaż jeden z podanych warunków jest prawdziwy . Widzimy, że mamy tu dwa porównania – pierwsze (a < 5) , dla zmiennej a równej siedemnaście zwraca fałsz , jednak drugie (a > 10) , zwraca prawdę (bo 17 > 10). Operacja logiczna OR dla takich parametrów zwraca prawdę (zobacz w tabeli przedstawionej powyżej), tak więc zostanie wyświetlony na ekranie odpowiedni tekst. Zapamiętaj więc – jeśli chcesz sprawdzić, czy którykolwiek z warunków jest spełniony, użyj operacji OR.
W sytuacji, gdy wykonanie działania ma zależeć od spełnienia wszystkich warunków, używamy natomiast instrukcji logicznej AND . Pokazane jest to przy okazji następnej instrukcji if . W tym wypadku chcemy wykonać działanie tylko wtedy, gdy zmienna a jest większa od dziesięciu i mniejsza od dwudziestu (czyli zawiera się w określonym przedziale). Pierwszy warunek (a > 10) , dla zmiennej a równej siedemnaście zwraca prawdę , drugi (a < 20) także zwraca prawdę . Patrząc do tabeli widzimy, że w takim przypadku wynikiem operacji AND jest prawda , więc na ekranie zostanie wyświetlony tekst o tym informujący. Zapamiętaj więc – jeśli chcesz wykonanie działania uzależnić od spełnienia wszystkich warunków, użyj operacji AND.
Przejdźmy teraz do omówienia ostatniej, najprostszej operacji logicznej, a mianowicie NOT . Jest ona po prostu negacją (zaprzeczeniem) danego parametru – tzn. że dla parametru prawda zwróci fałsz, natomiast dla parametru fałsz zwróci wartość prawda. Pokazuje to wyraźnie trzecia z instrukcji if w naszym programie. Warunek (a < 10) jest sformułowany tak, że dla zmiennej a równej siedemnaście, zwróci fałsz , czyli normalnie tekst nie zostałby wyświetlony na ekranie. Jednak w naszym przypadku występuje jeszcze negacja, która „zamienia” fałsz na prawdę i w efekcie na ekranie pojawi się odpowiedni napis.
8.5. Operacje na bitach
Pozostało jeszcze do omówienia zastosowanie poznanych operacji logicznych do wykonywania działań na bitach. Do zrozumienia (i wykorzystania w swoich programach) podanych w tym punkcie informacji konieczna będzie znajomość podstaw dwójkowego systemu zapisu liczb. Jeśli nie wiesz co to takiego to sugeruję, żebyś pominął teraz ten punkt i powrócił do niego po zaglądnięciu do książki z matematyki (ja to miałem w 7 lub 8 klasie podstawówki, teraz to chyba będzie w gimnazjum). Najpierw przedstawię operatory języka C stosowane do operacji na bitach:
Operacja
C
Pascal
OR
|
or
AND
&
and
XOR
^
xor
NOT
~
not
Wszystkie przedstawione w tabeli operacje mają taką samą zasadę działania. Pierwsze trzy operują na dwóch argumentach, natomiast ostatnia tylko na jednym. Algorytm działania tych pierwszych wygląda tak:
1. Najpierw zamieniasz oba argumenty na postać binarną.
2. Teraz bierzesz zerowe bity obu argumentów i wykonujesz odpowiednią operację logiczną
3. To, co otrzymałeś zapisujesz w zerowym bicie wyniku.
4. Punkty dwa i trzy wykonujesz kolejno dla pierwszego, drugiego, itd. bitu argumentu.
Natomiast algorytm działania operacji NOT jest dużo prostszy – po prostu neguje ona wszystkie bity argumentu. Czyli w efekcie tam, gdzie były zera, teraz będą jedynki i odwrotnie.
Być może nie zrozumiałeś wszystkiego z powyższego opisu. Jednak nie martw się – wszystko się wyjaśni przy analizie przykładowego programu:
#include <stdio.h>
void main(void)
{
char a=12, b=9;
printf(„%d and %d = %d
„, a, b, a & b);
printf(„%d or %d = %d
„, a, b, a | b);
printf(„%d xor %d = %d
„, a, b, a ^ b);
printf(„not %d = %d
„, b, ~b);
}
Każde z wywołań funkcji printf prezentuje inny operator. Zacznijmy od operacji AND.
Jak widzisz wykonujemy operację AND na dwóch argumentach – zmiennej a równej dwanaście i zmiennej b równej dziewięć. Po zapisaniu tego w postaci dwójkowej wygląda to następująco:
1100 (12 w systemie dziesiętnym)
AND
1001 (9 w systemie dziesiętnym)
====
1000 (8 w systemie dziesiętnym)
Na podstawie algorytmu działania przedstawionego powyżej pokażę w jaki sposób otrzymaliśmy w efekcie liczbę osiem. Najpierw zamieniamy obie liczby na postać binarną. Dla lepszego zobrazowania zapisałem je jedna nad drugą. Bierzemy teraz zerowy (ten z prawej) bit pierwszej liczby (12) – jest on równy zero. Następnie bierzemy zerowy bit drugiej liczby (9) – jest on równy jeden. Wykonujemy operację AND na tych danych – w efekcie otrzymujemy zero (zobacz w tabeli dla operacji AND), którą to zapisujemy jako zerowy bit wyniku. Teraz to samo wykonujemy dla pierwszych bitów obu liczb. Tym razem oba są równe zero, więc w wyniku także otrzymujemy zero. Następnie wykonujemy operację dla bitów numer trzy obu liczb. Wynikiem działania dla argumentów równych jeden i zero jest ponownie zero. Dokonujemy tej samej operacji dla bitów numer cztery obu liczb – w efekcie wykonania operacji AND na obu argumentach równych jeden, otrzymujemy jedynkę. Wynikiem całej operacji jest więc liczba 1000 w systemie dwójkowych. Liczba ta w systemie dziesiętnym jest równa osiem i właśnie ona zostanie wyświetlona na ekranie.
Następną operacją jest operacja OR . Po zapisaniu jej podobnie jak poprzednio otrzymujemy:
1100 (12 w systemie dziesiętnym)
OR
1001 (9 w systemie dziesiętnym)
====
1101 (13 w systemie dziesiętnym)
Algorytm działania jest identyczny jak poprzednio, tylko zamiast operacji AND wykonujemy operację OR. Wynikiem tej operacji jest jedynka, gdy którykolwiek z argumentów jest jedynką, stąd też tylko dla bitu pierwszego otrzymaliśmy zero, a reszta bitów jest równa jeden.
Przedostatnią operacją jest XOR :
1100 (12 w systemie dziesiętnym)
XOR
1001 (9 w systemie dziesiętnym)
====
0101 (5 w systemie dziesiętnym)
I znowu wykonujemy identyczne operacje, zamieniając tylko operację na XOR. Dla przypomnienia – wynikiem tej operacji jest jedynka, gdy tylko jeden z argumentów jest równy jeden.
Ostatnią operacją jest operacja NOT . Jak zapewne zauważyłeś po uruchomieniu programu, wynikiem operacji NOT 9 jest liczba minus dziesięć, którą binarną reprezentacją jest 11110110. A oto jak ją otrzymaliśmy:
NOT
00001001 (9 w systemie dziesiętnym)
========
11110110 (-10 w systemie dziesiętnym)
Jest to najprostsza z operacji logicznych – ja widać odwraca po prostu wszystkie bity argumentu.