C++1 1, r04-06, Szablon dla tlumaczy


Rozdział 4.
Wyrażenia i instrukcje

Program stanowi zestaw kolejno wykonywanych instrukcji. Jakość działania programu zależy od możliwości wykonywania określonego zestawu instrukcji w danych warunkach.

Z tego rozdziału dowiesz się:

Instrukcje

W C++ instrukcje kontrolują kolejność działania programu, obliczają wyrażenia lub nie robią nic (instrukcja pusta). Wszystkie instrukcje C++ kończą się średnikiem (nawet instrukcja pusta, która składa się wyłącznie ze średnika). Jedną z najczęściej występujących instrukcji jest instrukcja przypisania:

x = a + b;

W przeciwieństwie do znaczenia, jakie ma w algebrze, ta instrukcja nie oznacza tutaj, że x równa się a+b. Należy ją traktować jako „przypisz wartość sumy a i b do x” lub „przypisz a+b do x” lub „niech x równa się a+b.” Choć ta instrukcja wykonuje dwie czynności, nadal jest pojedynczą instrukcją (stąd tylko jeden średnik). Operator przypisania przypisuje to, co znajduje się po prawej stronie znaku równości elementowi znajdującemu się po lewej stronie.

Białe spacje

Białe spacje (tabulatory, spacje i znaki nowej linii) są w instrukcjach ignorowane. Omawiana poprzednio instrukcja przypisania może zostać zapisana jako:

x=a+b;

lub jako:

x =a

+ b ;

Choć ostatni zapis jest poprawny, jest równocześnie niemądry. Białe spacje mogą być używane w celu poprawienia czytelności programu lub stworzenia okropnego, niemożliwego do rozszyfrowania kodu. C++ daje do wyboru wiele możliwości, ale ich rozważne użycie zależy od ciebie.

Znaki białych spacji nie są widoczne. Gdy zostaną wydrukowane, na papierze będą widoczne jako odstępy.

Bloki i instrukcje złożone

Wszędzie tam, gdzie może znaleźć się instrukcja pojedyncza, może znaleźć się także instrukcja złożona, zwana także blokiem. Blok rozpoczyna się od otwierającego nawiasu klamrowego ({) i kończy nawiasem zamykającym (}). Choć każda instrukcja w bloku musi kończyć się średnikiem, sam blok nie wymaga jego zastosowania (jak pokazano w poniższym przykładzie):

{

temp = a;

a = b;

b = temp;

}

Ten blok kodu działa jak pojedyncza instrukcja i zamienia wartości w zmiennych a i b.

TAK

Jeśli użyłeś otwierającego nawiasu klamrowego, pamiętaj także o nawiasie zamykającym.

Kończ instrukcje średnikiem.

Używaj rozważnie białych spacji, tak aby kod był czytelny.

Wyrażenia

Wszystko, co staje się wartością, w C++ jest uważane za wyrażenie. Mówi się, że wyrażenie zwraca wartość. Skoro instrukcja 3+2; zwraca wartość 5, więc jest wyrażeniem. Wszystkie wyrażenia są jednocześnie instrukcjami.

Możesz zdziwić się, ile miejsc w kodzie kwalifikuje się jako wyrażenia. Oto trzy przykłady:

3.2 // zwraca wartość 3.2

PI // stała typu float zwracająca wartość 3.14

SecondsPerMinute // stała typu int zwracająca 60

Gdy założymy, że PI jest stałą, którą zainicjalizowałem wartością 3.14 i że SecondsPerMinute (sekund na minutę) jest stałą wynoszącą 60, wtedy wszystkie te trzy instrukcje są wyrażeniami.

Nieco bardziej skomplikowane wyrażenie

x = a + b;

nie tylko dodaje do siebie a oraz b, a wynik umieszcza w x, ale także zwraca wartość tego przypisania (nową wartość x). Zatem instrukcja przypisania także jest wyrażeniem. Ponieważ jest wyrażeniem, może wystąpić po prawej stronie operatora przypisania:

y = x = a + b;

Ta linia jest przetwarzana w następującym porządku:

Dodaj a do b.

Przypisz wynik wyrażenia a + b do x.

Przypisz rezultat wyrażenia przypisania, x = a + b, do y.

Jeśli a, b, x oraz y byłyby zmiennymi całkowitymi, zaś a miałoby wartość 2, a b miałoby wartość 5, wtedy zarówno zmiennej x, jak i y zostałaby przypisana wartość 7. Ilustruje to listing 4.1.

Listing 4.1. Obliczanie wyrażeń złożonych

0: #include <iostream>

1: int main()

2: {

3: using std::cout;

4: using std::endl;

5:

6: int a=0, b=0, x=0, y=35;

7: cout << "a: " << a << " b: " << b;

8: cout << " x: " << x << " y: " << y << endl;

9: a = 9;

10: b = 7;

11: y = x = a+b;

12: cout << "a: " << a << " b: " << b;

13: cout << " x: " << x << " y: " << y << endl;

14: return 0;

15: }

Wynik

a: 0 b: 0 x: 0 y: 35

a: 0 b: 7 x: 16 y: 16

Analiza

W linii 6. deklarowane i inicjalizowane są cztery zmienne. Ich wartości są wypisywane w liniach 7. i 8. W linii 9. zmiennej a jest przypisywana wartość 9. W linii 10., zmiennej b jest przypisywana wartość 7. W linii 11. zmienne a i b są sumowane, zaś wynik sumowania jest przypisywany zmiennej x. To wyrażenie (x = a+b) powoduje obliczenie sumy a oraz b i przypisanie jej do zmiennej x, wartość tego przypisania jest następnie przypisywana zmiennej y.

Operatory

Operator jest symbolem, który powoduje, że kompilator rozpoczyna działanie. Operatory działają na operandach, zaś wszystkie operandy w C++ są wyrażeniami. W C++ istnieje kilka kategorii operatorów. Dwie z tych kategorii to:

Operator przypisania

Operator przypisania (=) powoduje, że operand znajdujący się po lewej stronie operatora przypisania zmienia wartość na wartość operandu znajdującego się po prawej stronie operatora. To w[Author ID1: at Sat Oct 20 12:03:00 2001 ]W[Author ID1: at Sat Oct 20 12:03:00 2001 ]yrażenie:

x = a + b;

przypisuje operandowi x wynik dodawania wartości a i b.

Operand, który może wystąpić po lewej stronie operatora przypisania jest nazywany l-wartością (l-value). To, co [Author ID1: at Sat Oct 20 12:04:00 2001 ]Natomiast ten, który [Author ID1: at Sat Oct 20 12:04:00 2001 ]może znaleźć się po prawej stronie, jest nazywany[Author ID1: at Sat Oct 20 12:05:00 2001 ]e[Author ID1: at Sat Oct 20 12:05:00 2001 ] (jak można się domyślić), r-wartością (r-value).

Stałe są r-wartościami. Nie mogą być l-wartościami. Zatem możesz napisać:

x = 35; // OK

lecz nie możesz napisać:

35 = x; // błąd, 35 nie może być l-wartością!

L-wartość jest operandem, który może znaleźć się po lewej stronie wyrażenia. R-wartość jest operandem, który może występować po prawej stronie wyrażenia. Zwróć uwagę,[Author ID1: at Sat Oct 20 12:06:00 2001 ] że wszystkie l-wartości mogą być r-wartościami, ale że [Author ID1: at Sat Oct 20 12:06:00 2001 ]nie wszystkie r-wartości mogą być l-wartościami. Przykładem r-wartości, która nie jest l-wartością, może być literał. Zatem możesz napisać x = 5;, lecz nie możesz napisać 5 = x; (x może być l- lub r-wartością, lecz 5 może być tylko r-wartością).

Operatory matematyczne

Piątka operatorów matematycznych to: dodawanie (+), odejmowanie (-), mnożenie (*), dzielenie (/) oraz reszta z dzielenia (%).

Dodawanie i odejmowanie działają rutynowo, choć odejmowanie liczb całkowitych bez znaku może prowadzić do zadziwiających rezultatów gdy wynik będzie ujemny. Z czymś takim spotkałeś się w poprzednim rozdziale, kiedy opisywaliśmy przepełnienie (przewinięcie wartości). Listing 4.2 pokazuje, co się stanie gdy odejmiesz dużą liczbę całkowitą bez znaku od małej liczby całkowitej bez znaku.

Listing 4.2. Przykład odejmowania i przepełnienia wartości całkowitej

0: // Listing 4.2 - Demonstracja odejmowania

1: // i przepełnienia wartości całkowitej.

2: #include <iostream>

3:

4: int main()

5: {

6: using std::cout;

7: using std::endl;

8:

9: unsigned int difference;

10: unsigned int bigNumber = 100;

11: unsigned int smallNumber = 50;

12: difference = bigNumber - smallNumber;

13: cout << "Roznica to: " << difference;

14: difference = smallNumber - bigNumber;

15: cout << "\nTeraz roznica to: " << difference <<endl;

16: return 0;

17: }

Wynik

Roznica to: 50

Teraz roznica to: 4294967246

Analiza

Operator odejmowania jest wywoływany w linii 12., zaś wynik jest wypisywany w linii 13. (taki, jakiego mogliśmy oczekiwać). Operator odejmowania jest ponownie wywoływany w linii 14., jednak tym razem od małej liczby całkowitej bez znaku jest odejmowana duża liczba całkowita bez znaku. Wynik powinien być ujemny, ale ponieważ wartości są obliczane (i wypisywane) jako liczby całkowite bez znaku, efektem tej operacji jest przepełnienie, tak jak opisywaliśmy w poprzednim rozdziale. Ten temat jest szczegółowo omawiany w dodatku C, „Kolejność operatorów.”

Dzielenie całkowite i reszta z dzielenia

Dzielenie całkowite poznałeś w drugiej klasie szkoły podstawowej. Gdy w dzieleniu całkowitym podzielisz 21 przez 4 (21/4), otrzymasz w wyniku 5 (oraz pewną resztę).

Resztę z dzielenia całkowitego zwraca operator reszty z dzielenia (tzw. operator modulo). Aby otrzymać resztę, oblicz 21 modulo 4 (21 % 4). W wyniku otrzymasz 1.

Obliczanie reszty z dzielenia może być bardzo przydatne. Możesz na przykład zechcieć wypisywać komunikat po każdej dziesiątej akcji. Każda liczba, dla której wynikiem reszty z dzielenia przez 10 jest zero, stanowi pełną wielokrotność dziesięciu. Tak więc 1 % 10 wynosi 1, 2 % 10 wynosi 2, itd., aż do 10 % 10, które ponownie wynosi 0. 11 % 10 to znów 1, wzór ten powtarza się aż do następnej wielokrotności dziesięciu, którą jest liczba 20. 20 % 0 to ponownie 0. Tę technikę wykorzystujemy wewnątrz pętli, które zostaną omówione w rozdziale 7.

Często zadawane pytanie

Gdy dzielę 5/3, otrzymuję w wyniku 1. Czy coś robię nie tak?

Odpowiedź

Gdy dzielisz jedną liczbę całkowitą przez inną, w wyniku otrzymujesz także liczbę całkowitą. Zatem 5/3 wyniesie 1. (W rzeczywistości wynikiem jest 1 i reszta 2. Aby otrzymać resztę, spróbuj napisać 5%3, uzyskasz w ten sposób wartość 2.)

Aby uzyskać ułamkową wartość z dzielenia, musisz użyć zmiennych zmiennoprzecinkowych[Author ID1: at Sat Oct 20 12:14:00 2001 ]ozycyjnych[Author ID1: at Sat Oct 20 12:15:00 2001 ].

5.0/3.0 da wartość zmiennoprzecinkow[Author ID1: at Sat Oct 20 12:15:00 2001 ]ą[Author ID1: at Sat Oct 20 12:16:00 2001 ]ozycyj[Author ID1: at Sat Oct 20 12:15:00 2001 ][Author ID1: at Sat Oct 20 12:16:00 2001 ] 1.66667.

Jeśli zmiennoprzecinkowa[Author ID1: at Sat Oct 20 12:16:00 2001 ] jest dzielna lub dzielnikozycyjna[Author ID1: at Sat Oct 20 12:16:00 2001 ], kompilator wygeneruje zmiennoprzecinkowy[Author ID1: at Sat Oct 20 12:16:00 2001 ]ozycyjny[Author ID1: at Sat Oct 20 12:16:00 2001 ] iloraz.

Łączenie operatora przypisania z operatorem matematycznym

Często zdarza się, że chcemy do zmiennej dodać wartość, zaś wynik umieścić z powrotem w tej zmiennej. Jeśli masz zmienną myAge (mój wiek) i chcesz zwiększyć jej wartość o dwa, możesz napisać:

int myAge = 5;

int temp;

temp = myAge + 2; // czyli 5 + 2 jest umieszczane w zmiennej temp

myAge = temp; // wynik umieszczamy z powrotem w myAge

Ta metoda jest jednak bardzo żmudna i nieefektywna. W C++ istnieje możliwość umieszczenia tej samej zmiennej po obu stronach operatora przypisania; w takim przypadku poprzedni przykład można zapisać jako:

myAge = myAge + 2;

Jest to dużo lepsza metoda. W algebrze to wyrażenie nie miałoby sensu, ale w C++ jest traktowane jako „dodaj dwa do wartości zawartej w zmiennej myAge, zaś wynik umieść ponownie w tej zmiennej”.

Jeszcze prostsze w zapisie, choć może nieco trudniejsze do odczytania, jest:

myAge += 2;

Operator += sumuje r-wartość z l-wartością, zaś wynik umieszcza ponownie w l-wartości. Ten operator wymawia się jako „plus-równa się”, zatem cała instrukcja powinna zostać odczytana jako „myAge plus-równa się dwa”. Jeśli zmienna myAge miałaby początkowo wartość 4, to po wykonaniu tej instrukcji przyjęłaby wartość 6.

Oprócz operatora += istnieją także operatory -= (odejmowania), /= (dzielenia), *= (mnożenia), %= (reszty z dzielenia) i inne.

Inkrementacja i dekrementacja

Najczęściej dodawaną (i odejmowaną), z ponownym przypisaniem wyniku zmiennej, wartością jest 1. W C++ zwiększenie wartości o jeden jest nazywane inkrementacją, zaś zmniejszenie o jeden — dekrementacją. Służą do tego specjalne operatory.

Operator inkrementacji (++) zwiększa wartość zmiennej o jeden, zaś operator dekrementacji (--) zmniejsza ją o jeden. Jeśli chcemy inkrementować zmienną C, możemy użyć następującej instrukcji:

C++; // zaczynamy od C i inkrementujemy

Ta instrukcja stanowi odpowiednik bardziej jawnie zapisanej operacji:

C = C + 1;

którą, jak wiemy, możemy zapisać w nieco prostszy sposób:

C += 1;

UWAGA Jak można się domyślić, język C++ otrzymał swoją nazwę dzięki zastosowaniu operatora inkrementacji do nazwy języka, od którego pochodzi (C). C++ jest kolejną, poprawioną wersją języka C.

Przedrostki i przyrostki

Zarówno operator inkrementacji (++), jak i dekrementacji (--) występuje w dwóch odmianach: przedrostkowej i przyrostkowej. Odmiana przedrostkowa jest zapisywana przed nazwą zmiennej (++myAge), zaś odmiana przyrostkowa — po niej (myAge++).

W przypadku instrukcji prostej nie ma znaczenia, której wersji użyjesz, jednak w wyrażeniach złożonych, gdy inkrementujesz (lub dekrementujesz) zmienną, a następnie przypisujesz rezultat innej zmiennej, różnica jest bardzo ważna. Operator przedrostkowy jest obliczany przed przypisaniem, zaś operator przyrostkowy — po przypisaniu.

Operator przedrostkowy działa następująco: zwiększ wartość zmiennej i zapamiętaj ją. Operator przyrostkowy działa inaczej: zapamiętaj pierwotną wartość zmiennej, po czym zwiększ wartość w zmiennej.

Na początku może to wydawać się dość niezrozumiałe, ale jeśli x jest zmienną całkowitą o wartości 5, to gdy napiszesz

int a = ++x;

poinformujesz kompilator, by inkrementował zmienną x (nadając jej wartość 6), po czym pobrał tę wartość i przypisał ją zmiennej a. Zatem po wykonaniu tej instrukcji zarówno zmienna x, jak i zmienna a mają wartość 6.

Jeśli następnie napiszesz

int b = x++;

to poinformujesz kompilator, by pobrał wartość zmiennej x (wynoszącą 6) i przypisał ją zmiennej b, po czym powrócił do zmiennej x i inkrementował ją. W tym momencie zmienna b ma wartość 6, a zmienna x ma wartość 7. Zastosowanie i działanie obu wersji operatora przedstawia listing 4.3.

Listing 4.3. Przykład działania operatora przedrostkowego i przyrostkowego

0: // Listing 4.3 - demonstruje użycie

1: // przedrostkowych i przyrostkowych operatorów

2: // inkrementacji i dekrementacji

3: #include <iostream>

4: int main()

5: {

6: using std::cout;

7:

8: int myAge = 39; // inicjalizujemy dwie zmienne całkowite

9: int yourAge = 39;

10: cout << "Ja mam: " << myAge << " lat.\n";

11: cout << "Ty masz: " << yourAge << " lat\n";

12: myAge++; // inkrementacja przyrostkowa

13: ++yourAge; // inkrementacja przedrostkowa

14: cout << "Minal rok...\n";

15: cout << "Ja mam: " << myAge << " lat.\n";

16: cout << "Ty masz: " << yourAge << " lat\n";

17: cout << "Minal kolejny rok\n";

18: cout << "Ja mam: " << myAge++ << " lat.\n";

19: cout << "Ty masz: " << ++yourAge << " lat\n";

20: cout << "Wypiszmy to jeszcze raz.\n";

21: cout << "Ja mam: " << myAge << " lat.\n";

22: cout << "Ty masz: " << yourAge << " lat\n";

23: return 0;

24: }

Wynik

Ja mam: 39 lat.

Ty masz: 39 lat

Minal rok...

Ja mam: 40 lat.

Ty masz: 40 lat

Minal kolejny rok

Ja mam: 40 lat.

Ty masz: 41 lat

Wypiszmy to jeszcze raz.

Ja mam: 41 lat.

Ty masz: 41 lat

Analiza

W liniach 8. i 9. deklarujemy dwie zmienne całkowite i inicjalizujemy je wartością 39. Ich wartości są wypisywane w liniach 10. i 11.

W linii 12. zmienna myAge (mój wiek) jest inkrementowana za pomocą operatora przyrostkowego, zaś w linii 13. zmienna yourAge (twój wiek) jest inkrementowana za pomocą operatora przedrostkowego. Wyniki wypisywane w liniach 15. i 16. są identyczne (40).

W linii 18. zmienna myAge jest inkrementowana jako część instrukcji wypisywania danych, za pomocą operatora przyrostkowego. Ponieważ jest to operator przyrostkowy, inkrementacja odbywa się już po wypisaniu tekstu, dlatego ponownie wypisywana jest wartość 40. Dla odróżnienia od linii 18., w linii 19. zmienna yourAge jest inkrementowana za pomocą operatora przedrostkowego. Ponieważ w takim przypadku inkrementacja odbywa się przed wypisaniem tekstu, zostaje wypisana wartość 41.

Na zakończenie, w liniach 21. i 22., ponownie wypisywane są wartości zmiennych. Ponieważ instrukcje inkrementacji zostały dokończone, zmienna myAge, podobnie jak zmienna yourAge przyjmuje wartość 41.

Kolejność działań

Które działanie instrukcji złożonej, takiej jak ta

x = 5 + 3 * 8;

jest wykonywane jako pierwsze: dodawanie czy mnożenie? Gdyby najpierw wykonywane było dodawanie, wynik wynosiłby 8 * 8, czyli 64. Gdyby najpierw wykonywane było mnożenie, uzyskalibyśmy wynik 5 + 24, czyli 29.

Każdy operator posiada swój priorytet, który określa kolejność wykonywania działań. Pełną listę priorytetów operatorów można znaleźć w dodatku C.

Mnożenie ma priorytet nad dodawaniem, więc w tym przypadku wartością wyrażenia jest 29.

Gdy dwa operatory matematyczne osiągają ten sam priorytet, są obliczane w kolejności od lewej do prawej. Zatem w wyrażeniu

x = 5 + 3 + 8 * 9 + 6 * 4;

mnożenia są wykonywane jako pierwsze (najpierw lewe, potem prawe). Otrzymujemy 8*9 = 72 oraz 6*4 = 24. Teraz wyrażenie można zapisać jako

x = 5 + 3 + 72 + 24.

Następnie obliczane są dodawania, od lewej do prawej: 5 + 3 = 8; 8 + 72 = 80; 80 + 24 = 104.

Bądź ostrożny. Niektóre operatory, takie jak operator przypisania, są obliczane w kolejności od prawej do lewej!

Co zrobić gdy kolejność wykonywania działań nie odpowiada naszym potrzebom? Weźmy na przykład takie wyrażenie:

TotalSeconds = NumMinutesToThink + NumMinutesToType * 60

W tym wyrażeniu nie chcemy mnożyć zmiennej NumMinutesToType (ilość minut wpisywania) przez 60, a następnie dodawać otrzymanej wartości do zmiennej NumMinutesToThink (ilość minut namysłu). Chcemy zsumować obie zmienne, aby otrzymać łączną ilość minut, a dopiero potem przemnożyć ją przez ilość sekund w minucie, w celu otrzymania łącznej ilości sekund (TotalSeconds).

W tym przypadku, w celu zmiany kolejności działań użyjemy nawiasów. Działania na elementach w nawiasach są zawsze wykonywane przed innymi operacjami matematycznymi. Zatem zamierzony wynik uzyskamy dopiero wtedy, gdy napiszemy:

TotalSeconds = (NumMinutesToThink + NumMinutesToType) * 60

Zagnieżdżanie nawiasów

W przypadku złożonych wyrażeń można zagnieżdżać nawiasy jeden wewnątrz drugiego. Na przykład, przed obliczeniem łącznej ilości „osobosekund” (TotalPersonSeconds) możesz zechcieć obliczyć łączną ilość sekund oraz łączną ilość osób zajętych pracą:

TotalPersonSeconds = ( ( (NumMinutesToThink+[Author ID1: at Sat Oct 20 12:36:00 2001 ]*[Author ID1: at Sat Oct 20 12:36:00 2001 ]NumMinutesToType) * 60) *

(PeopleInTheOffice + PeopleOnVacation) )

To złożone wyrażenie jest odczytywane od wewnątrz na zewnątrz. Najpierw sumowane są zmienne NumMinutesToThink oraz NumMinutesToType, gdyż znajdują się w wewnętrznych nawiasach. Potem ta suma jest mnożona przez 60. Następnie sumowane są zmienne PeopleInTheOffice (osoby w biurze) oraz PeopleOnVacation (osoby na urlopie). Na koniec łączna ilość osób jest przemnażana przez łączną ilość sekund.

Z tym przykładem wiąże się jeszcze jedno ważne zagadnienie. To wyrażenie jest łatwe do zrozumienia przez komputer, lecz jest bardzo trudne do odczytania, zrozumienia lub zmodyfikowania przez człowieka. Oto to samo wyrażenie, przepisane z użyciem kilku tymczasowych zmiennych całkowitych:

TotalMinutes = NumMinutesToThink + NumMinutesToType;

TotalSeconds = TotalMinutes * 60;

TotalPeople = PeopleInTheOffice + PeopleOnVacation;

TotalPersonSeconds = TotalPeople * TotalSeconds;

Napisanie tego przykładu wymaga więcej czasu do napisania i skorzystania z większej ilości tymczasowych zmiennych, lecz sprawi, że będzie on dużo łatwiejszy do zrozumienia. Gdy dodasz do niego komentarz, opisujący, do czego służy ten kod oraz gdy zamienisz wartość 60 na stałą symboliczną, otrzymasz łatwy do zrozumienia i modyfikacji kod.

TAK

NIE

Pamiętaj, że wyrażenia mają wartość.

Używaj operatora przedrostkowego (++zmienna) do inkrementacji lub dekrementacji zmiennej przed jej użyciem w wyrażeniu.

Używaj operatora przyrostkowego (zmienna++) do inkrementacji lub dekrementacji zmiennej po jej użyciu w wyrażeniu.

W celu zmiany kolejności działań używaj nawiasów.

Nie zagnieżdżaj nawiasów zbyt głęboko, gdyż wyrażenie stanie się zbyt trudne do zrozumienia i modyfikacji.

Prawda i fałsz

W poprzednich wersjach C++, prawda i fałsz były reprezentowane jako liczby całkowite; w standardzie ANSI wprowadzono nowy typ: bool. Typ ten może mieć tylko dwie wartości, true (prawda) oraz false (fałsz).

Można sprawdzić prawdziwość każdego wyrażenia. Wyrażenia, których matematycznym wynikiem jest zero, zwracają wartość false. Wszystkie inne wyrażenia zwracają wartość true.

UWAGA Wiele kompilatorów oferowało typ bool już wcześniej, był on wewnętrznie reprezentowany jako typ long int i miał rozmiar czterech bajtów. Nowe, zgodne z ANSI kompilatory często korzystają z jednobajtowych zmiennych typu bool.

Operatory relacji

Operatory relacji są używane do sprawdzania, czy dwie liczby są równe, albo czy jedna z nich jest większa lub mniejsza od drugiej. Każdy operator relacji zwraca prawdę lub fałsz. Operatory relacji zostaną przedstawione nieco dalej, w tabeli 4.1.

UWAGA Wszystkie operatory relacji zwracają wartość typu bool, czyli wartość true albo false. W poprzednich wersjach C++ operatory te zwracały albo wartość 0 dla fałszu, albo wartość różną od zera (zwykle 1) dla prawdy.

Jeśli zmienna całkowita myAge ma wartość 45, zaś zmienna całkowita yourAge ma wartość 50, możesz sprawdzić, czy są równe, używając operatora „równa się”:

myAge == yourAge; // czy wartość myAge jest równa wartości yourAge?

Wyrażenie ma wartość false (fałsz), gdyż wartości tych zmiennych nie są równe. Z kolei wyrażenie

myAge < yourAge; // czy myAge jest mniejsze od yourAge?

ma wartość true (prawda).

OSTRZEŻENIE Wielu początkujących programistów C++ myli operator przypisania (=) z operatorem relacji równości (==). Może to prowadzić do uporczywych i trudnych do wykrycia błędów w programach.

Sześć operatorów relacji to: równe (==), mniejsze (<), większe (>), mniejsze lub równe (<=), większe lub równe (>=) oraz różne (!=). Zostały one zebrane (wraz z przykładami użycia w kodzie) w tabeli 4.1.

Tabela 4.1. Operatory relacji

Nazwa

Operator

Przykład

Wynik

Równe

==

100 == 50;

50 == 50;

false (fałsz)

true (prawda)

Nie równe

!=

100 != 50;

50 != 50;

true (prawda)

false (fałsz)

Większe

>

100 > 50;

50 > 50;

true (prawda)

false (fałsz)

Większe lub równe

>=

100 >= 50;

50 >= 50;

true (prawda)

true (prawda)

Mniejsze

<

100 < 50;

50 < 50;

false (fałsz)

false (fałsz)

Mniejsze lub równe

<=

100 <= 50;

50 <= 50;

false (fałsz)

true (prawda)

TAK

NIE

Pamiętaj, że operatory relacji zwracają wartość true (prawda) lub false (fałsz).

Nie myl operatora przypisania (=) z operatorem relacji równości (==). Jest to jeden z najczęstszych błędów popełnianych przez programistów C++. Strzeż się go.

Instrukcja if

Program jest wykonywany linia po linii, w takiej kolejności, w jakiej linie te występują w tekście kodu źródłowego. Instrukcja if umożliwia sprawdzenie spełnienia warunku (na przykład, czy dwie zmienne są równe) i przejście do wykonania innej części kodu.

Najprostsza forma instrukcji if jest następująca:

if (wyrażenie)

instrukcja;

Wyrażenie w nawiasach może być całkowicie dowolne, ale najczęściej jest to jedno z wyrażeń relacji. Jeśli wyrażenie to ma wartość false, wtedy instrukcja jest pomijana. Jeśli wyrażenie jest prawdziwe (ma wartość true), wtedy instrukcja jest wykonywana. Weźmy poniższy przykład:

if (bigNumber > smallNumber)

bigNumber = smallNumber;

Ten kod porównuje zmienną bigNumber (duża liczba) ze zmienną smallNumber (mała liczba). Jeśli wartość zmiennej bigNumber jest większa, w drugiej linii tej zmiennej jest przypisywana wartość zmiennej smallNumber.

Ponieważ blok instrukcji ujętych w nawiasy klamrowe stanowi odpowiednik instrukcji pojedynczej, warunkowo wykonywany fragment kodu może być dość rozbudowany:

if (wyrażenie)

{

instrukcja1;

instrukcja2;

instrukcja3;

}

Oto prosty przykład wykorzystania tej możliwości:

if (bigNumber > smallNumber)

{

bigNumber = smallNumber;

std::cout << "duza liczba: " << bigNumber << "\n";

std::cout << "mala liczba: " << smallNumber << "\n";

}

Tym razem, jeśli zmienna bigNumber jest większa od zmiennej smallNumber, przypisywana jest jej wartość zmiennej smallNumber, a ponadto wypisywany jest informacyjny komunikat. Listing 4.4 przedstawia szczegółowo przykład warunkowego wykonywania kodu (z zastosowaniem operatorów relacji).

Listing 4.4. Przykład warunkowego wykonania kodu (z zastosowaniem operatorów relacji)

0: // Listing 4.5 - demonstruje instrukcje if

1: // używane z operatorami relacji

2: #include <iostream>

3: int main()

4: {

5: using std::cout;

6: using std::cin;

7:

8: int MetsScore, YankeesScore;

9: cout << "Wpisz wynik dla Metsow: ";

10: cin >> MetsScore;

11:

12: cout << "\nWpisz wynik dla Yankees: ";

13: cin >> YankeesScore;

14:

15: cout << "\n";

16:

17: if (MetsScore > YankeesScore)

18: cout << "Let's Go Mets!\n";

19:

20: if (MetsScore < YankeesScore)

21: {

22: cout << "Go Yankees!\n";

23: }

24:

25: if (MetsScore == YankeesScore)

26: {

27: cout << "Remis? Eeee, nie moze byc...\n";

28: cout << "Podaj mi prawdziwy wynik dla Yanks: ";

29: cin >> YankeesScore;

30:

31: if (MetsScore > YankeesScore)

32: cout << "Wiedzialem! Let's Go Mets!";

33:

34: if (YankeesScore > MetsScore)

35: cout << "Wiedzialem! Go Yanks!";

36:

37: if (YankeesScore == MetsScore)

38: cout << "Coz, rzeczywiscie byl remis!";

39: }

40:

41: cout << "\nDzieki za informacje.\n";

42: return 0;

43: }

Wynik

Wpisz wynik dla Metsow: 10

Wpisz wynik dla Yankees: 10

Remis? Eeee, nie moze byc...

Podaj mi prawdziwy wynik dla Yanks: 8

Wiedzialem! Let's Go Mets!

Dzieki za informacje.

Analiza

Ten program pyta użytkownika o wyniki spotkań dwóch drużyn baseballowych; wyniki są przechowywane w zmiennych całkowitych. Te zmienne są porównywane w instrukcjach if w liniach 17., 20. i 25. (W poprzednich wydaniach książki Yankees występowali przeciw Red Sox. W tym roku mamy inną serię, więc zaktualizowałem przykład!)

Jeśli jeden z wyników jest wyższy niż drugi, wypisywany jest komunikat informacyjny. Jeśli wyniki są równe, wtedy program przechodzi do bloku kodu zaczynającego się w linii 25[Author ID1: at Sat Oct 20 13:01:00 2001 ].9[Author ID1: at Sat Oct 20 13:01:00 2001 ] i kończącego w linii 39. Pojawia się w nim prośba o ponowne podanie drugiego wyniku, po czym wyniki są porównywane jeszcze raz.

Zwróć uwagę, że gdyby początkowy wynik Yankees był większy niż wynik Metsów, wtedy w instrukcji if w linii 17. otrzymalibyśmy wynik false, co spowodowałoby że linia 18. nie zostałaby wykonana. Test w linii 20. miałby wartość true, więc wykonana zostałaby instrukcja w linii 22.. Następnie zostałaby wykonana instrukcja if w linii 25. i jej wynikiem byłoby false (jeśli wynikiem w linii 17. była prawda). Tak więc program pominąłby cały blok, aż do linii 39.

Ten przykład ilustruje że otrzymanie wyniku true w jednej z instrukcji if nie powoduje zaprzestania sprawdzania pozostałych instrukcji if.

Zauważ, że wykonywaną zawartością dwóch pierwszych instrukcji if są pojedyncze linie (wypisujące „Let's Go Mets!” lub „Go Yankees!”). W pierwszym przykładzie (w linii 18.) nie umieściłem linii w nawiasach klamrowych, gdyż pojedyncza instrukcja nie wymaga ich zastosowania. Nawiasy klamrowe są jednak dozwolone, więc użyłem ich w liniach 21. i 23.

OSTRZEŻENIE Wielu początkujących programistów C++ nieświadomie umieszcza średnik za nawiasem zamykającym instrukcję if:

if(SomeValue < 10);

SomeValue = 10;

Zamiarem programisty było tu sprawdzenie, czy zmienna SomeValue (jakaś wartość) jest mniejsza niż 10, i gdy warunek ten zostałby spełniony, przypisalibyśmy tej zmiennej minimalną wartość 10. Uruchomienie tego fragmentu kodu pokazuje, że zmienna SomeValue jest zawsze ustawiana na 10! Dlaczego? Ponieważ instrukcja if kończy się średnikiem (czyli instrukcją pustą).

Pamiętaj, że wcięcia w kodzie źródłowym nie mają dla kompilatora żadnego znaczenia. Ten fragment mógłby zostać zapisany (bardziej poprawnie) jako:

if(SomeValue < 10) // sprawdzenie

; // nic nie rób

SomeValue = 10; // przypisz

Usunięcie średnika występującego w pierwszym przykładzie spowoduje, że druga linia stanie się częścią instrukcji if i kod zadziała zgodnie z planem.

Styl wcięć

Listing 4.3 pokazuje jeden ze stylów „wcinania” instrukcji if. Nie ma chyba jednak lepszego sposobu[Author ID1: at Sat Oct 20 13:12:00 2001 ]powodu[Author ID1: at Sat Oct 20 13:12:00 2001 ] na wszczęcie wojny religijnej niż zapytanie grupy programistów, jaki jest najlepszy styl wyrównywania nawiasów klamrowych. Choć dostępne są tuziny ich odmian, wygląda na to, że najczęściej stosowane są trzy z nich:

if (wyrażenie){

instrukcje

}

if (wyrażenie)

{

instrukcje

}

if (wyrażenie)

{

instrukcje

}

W tej książce stosujemy drugą z podanych wyżej wersji, gdyż uważam, że najlepiej pokazuje gdzie zaczyna się, a gdzie się kończy blok instrukcji. Pamiętaj jednak, że nie ma znaczenia który styl sam wybierzesz, o ile tylko będziesz go konsekwentnie stosował.

else

Często zdarza się, że w swoim programie chcesz wykonać jakiś [Author ID1: at Sat Oct 20 13:17:00 2001 ]fragment kodu, jeżeli spełniony zostanie pewien [Author ID1: at Sat Oct 20 13:17:00 2001 ]warunek, oraz inny fragment kodu, gdy warunek ten nie zostanie spełniony. Na listingu 4.4 chcieliśmy wypisać komunikat (Let's Go Mets!), pod warunkiem, że pierwszy test (MetsScore > YankeesScore) da wartość true, lub inny komunikat (Go Yankees!), gdy ten test da wartość false.

Pokazana już wcześniej metoda — sprawdzenie najpierw pierwszego warunku, a potem drugiego — działa poprawnie, lecz jest nieco żmudna. Dzięki zastosowaniu słowa kluczowego else możemy to zamienić na bardziej czytelny fragment kodu:

if (wyrażenie)

instrukcja;

else

instrukcja;

Użycie słowa kluczowego else demonstruje listing 4.5.

Listing 4.5. Użycie słowa kluczowego else

0: // Listing 4.5 - demonstruje instrukcję if

1: // z klauzulą else

2: #include <iostream>

3: int main()

4: {

5: using std::cout;

6: using std::cin;

7:

8: int firstNumber, secondNumber;

9: cout << "Prosze wpisac wieksza liczbe: ";

10: cin >> firstNumber;

11: cout << "\nProsze wpisac mniejsza liczbe: ";

12: cin >> secondNumber;

13: if (firstNumber > secondNumber)

14: cout << "\nDzieki!\n";

15: else

16: cout << "\nPomylka! Druga liczba jest wieksza!";

17:

18: return 0;

19: }

Wynik

Prosze wpisac wieksza liczbe: 10

Prosze wpisac mniejsza liczbe: 12

Pomylka! Druga liczba jest wieksza!

Analiza

Obliczany jest warunek w instrukcji if w linii 13. Jeśli warunek jest spełniony (prawdziwy), wykonywana jest instrukcja w linii 14.; jeśli nie jest spełniony (jest fałszywy), wykonywana jest instrukcja w linii 16. Gdyby klauzula else w linii 15. zostałaby[Author ID1: at Sat Oct 20 13:21:00 2001 ] usunięta, wtedy instrukcja w linii 16. byłaby wykonywana zawsze, bez względu na to, czy warunek instrukcji if byłby spełniony, czy nie. Pamiętaj że instrukcja if kończy się po linii 14. Gdyby nie było else, linia 16. byłaby po prostu kolejną linią programu.

Pamiętaj, że obie instrukcje wykonywane warunkowo można zastąpić blokami kodu ujętymi w nawiasy klamrowe.

Instrukcja if

Składnia instrukcji if jest następująca:

Forma 1

if (wyrażenie)

instrukcja;

następna instrukcja;

Jeśli wyrażenie ma wartość true, to instrukcja jest wykonywana i program przechodzi do wykonania następnej instrukcji. Jeśli wyrażenie nie jest prawdziwe, instrukcja jest ignorowana i program przechodzi bezpośrednio do następnej instrukcji.

Pamiętaj, że instrukcja może być pojedynczą instrukcją zakończoną średnikiem lub blokiem instrukcji ujętym w nawiasy klamrowe.

Forma 2

if (wyrażenie)

instrukcja1;

else

instrukcja2;

następna instrukcja;

Jeśli wyrażenie ma wartość true, wykonywana jest instrukcja1; w przeciwnym razie wykonywana jest instrukcja2. Następnie program przechodzi do wykonania następnej instrukcji.

Przykład 1

if (SomeValue < 10)

cout << "SomeValue jest mniejsze niż 10";

else

cout << "SomeValue nie jest mniejsze niż 10";

cout << "Gotowe." << endl;

Zaawansowane instrukcje if

Warto zauważyć, że w klauzuli if lub else może być zastosowana dowolna instrukcja, nawet inna instrukcja if lub else. Z tego powodu możemy natrafić na złożone instrukcje if, przyjmujące postać:

if (wyrażenie1)

{

if (wyrażenie2)

instrukcja1;

else

{

if (wyrażenie3)

instrukcja2;

else

instrukcja3;

}

}

else

instrukcja4;

Ta rozbudowana instrukcja if działa następująco: jeśli wyrażenie1 ma wartość true i wyrażenie2 ma wartość true, wykonaj instrukcję1. Jeśli wyrażenie1 ma wartość true, lecz wyrażenie2 ma wartość false, wtedy, jeśli wyrażenie3 ma wartość true, wykonaj instrukcję2. Jeśli wyrażenie1 ma wartość true, lecz wyrażenie2 i wyrażenie3 mają wartość false, wtedy wykonaj instrukcję3. Na zakończenie, jeśli wyrażenie1 ma wartość false, wykonaj instrukcję4. Jak widać, złożone instrukcje if mogą wprawiać w zakłopotanie!

Przykład takiej złożonej instrukcji if zawiera listing 4.6.

Listing 4.6. Złożona, zagnieżdżona instrukcja if

0: // Listing 4.6 - a złożona zagnieżdżona

1: // instrukcja if

2: #include <iostream>

3: int main()

4: {

5: // Poproś o dwie liczby.

6: // Przypisz je zmiennym bigNumber i littleNumber

7: // Jeśli bigNumber jest większe niż littleNumber,

8: // sprawdź, czy się dzielą bez reszty.

9: // Jeśli tak, sprawdź, czy są to te same liczby.

10:

11: using namespace std;

12:

13: int firstNumber, secondNumber;

14: cout << "Wpisz dwie liczby.\nPierwsza: ";

15: cin >> firstNumber;

16: cout << "\nDruga: ";

17: cin >> secondNumber;

18: cout << "\n\n";

19:

20: if (firstNumber >= secondNumber)

21: {

22: if ((firstNumber%secondNumber) == 0) // dziela sie bez reszty?

23: {

24: if (firstNumber == secondNumber)

25: cout << "One sa takie same!\n";

26: else

27: cout << "One dziela sie bez reszty!\n";

28: }

29: else

30: cout << "One nie dziela sie bez reszty!\n";

31: }

32: else

33: cout << "Hej! Druga liczba jest wieksza!\n";

34: return 0;

35: }

Wynik

Wpisz dwie liczby.

Pierwsza: 10

Druga: 2

One dziela sie bez reszty!

Analiza

Program prosi o wpisanie dwóch liczb, jednej po drugiej. Następnie są one porównywane. Pierwsza instrukcja if, w linii 20., sprawdza, czy pierwsza liczba jest większa lub równa drugiej. Jeśli nie, wykonywana jest klauzula else w linii 32.

Jeśli pierwsza instrukcja if jest prawdziwa, wykonywany jest blok kodu zaczynający się w linii 21., po czym w linii 22. przeprowadzany jest kolejny test w instrukcji if. W tym przypadku sprawdzamy, czy reszta z dzielenia pierwszej liczby przez drugą wynosi zero, to jest czy obie liczby są przez siebie podzielne. Jeśli tak, liczby te mogą być takie same lub mogą być podzielne przez siebie. Instrukcja if w linii 24. sprawdza, czy te liczby są równe i w obu przypadkach wyświetla odpowiedni komunikat.

Jeśli warunek instrukcji if w linii 22. nie zostanie spełniony, wtedy wykonywana jest instrukcja else w linii 29.

Użycie nawiasów klamrowych w zagnieżdżonych instrukcjach if

Choć dozwolone jest pomijanie nawiasów klamrowych w instrukcjach if zawierających tylko pojedyncze instrukcje, i choć dozwolone jest zagnieżdżanie instrukcji if:

if (x > y) // gdy x jest większe od y

if (x < z) // oraz gdy x jest mniejsze od z

x = y; // wtedy przypisz zmiennej x wartość zmiennej y

lecz[Author ID1: at Sat Oct 20 13:50:00 2001 ]może to powodować zbyt dużo problemów ze zrozumieniem struktury kodu w przypadku, gdy piszesz duże zagnieżdżone instrukcje. Pamiętaj, białe spacje i wcięcia są ułatwieniem dla programisty, lecz nie stanowią żadnej różnicy dla kompilatora. Łatwo jest się pomylić i błędnie wstawić instrukcję else do niewłaściwej instrukcji if. Problem ten ilustruje listing 4.7.

Listing 4.7. Przykład: nawiasy klamrowe ułatwiają zorientowanie się, które instrukcje else należą do których instrukcji if.

0: // Listing 4.7 - demonstruje, dlaczego nawiasy klamrowe

1: // mają duże znaczenie w zagnieżdżonych instrukcjach if

2: #include <iostream>

3: int main()

4: {

5: int x;

6: std::cout << "Wpisz liczbe mniejsza niz 10 lub wieksza niz 100: ";

7: std::cin >> x;

8: std::cout << "\n";

9:

10: if (x >= 10)

11: if (x > 100)

12: std::cout << "Wieksza niz 100, Dzieki!\n";

13: else // nie tego else chcieliśmy!

14: std::cout << "Mniejsza niz 10, Dzieki!\n";

15:

16: return 0;

17: }

Wynik

Wpisz liczbe mniejsza niz 10 lub wieksza niz 100: 20

Mniejsza niz 10, Dzieki!

Analiza

Programista miał zamiar poprosić o liczbę mniejszą niż 10 lub większą od 100, sprawdzić czy wartość jest poprawna, po czym wypisać podziękowanie.

Jeśli instrukcja if w linii 10. jest prawdziwa, zostaje wykonana następna instrukcja (w linii 11.). W tym przypadku linia 11. jest wykonywana, gdy wprowadzona liczba jest większa niż 10. Linia 11. także zawiera instrukcję if. Ta instrukcja jest prawdziwa, gdy wprowadzona liczba jest większa od 100. Jeśli liczba jest większa niż 100, wtedy wykonywana jest instrukcja w linii 12.

Jeśli wprowadzona liczba jest mniejsza od 10, wtedy instrukcja if w linii 10. daje wynik false i program przechodzi do następnej linii po instrukcji if, czyli w tym przypadku do linii 16[Author ID1: at Sat Oct 20 13:55:00 2001 ]7[Author ID1: at Sat Oct 20 13:55:00 2001 ]. Jeśli wpiszesz liczbę mniejszą niż 10, otrzymasz wynik:

Wpisz liczbe mniejsza niz 10 lub wieksza niz 100: 9

Klauzula else w linii 13. miała być dołączona do instrukcji if w linii 10., i w związku z tym została odpowiednio wcięta. Jednak w rzeczywistości ta instrukcja else jest dołączona do instrukcji if w linii 11., co powoduje, że w programie występuje subtelny błąd.

Błąd jest subtelny, gdyż kompilator go nie zauważy i nie zgłosi. Jest to w pełni poprawny program języka C++, lecz nie wykonuje tego, do czego został stworzony. Na dodatek, w większości testów przeprowadzanych przez programistę będzie działał poprawnie. Dopóki wprowadzane będą liczby większe od 100, program będzie działał poprawnie.

Listing 4.8 przedstawia rozwiązanie tego problemu - wstawienie koniecznych nawiasów klamrowych.

Listing 4.8. Przykład właściwego użycia nawiasów klamrowych w instrukcji if

0: // Listing 4.8 - demonstruje właściwe użycie nawiasów

1: // klamrowych w zagnieżdżonych instrukcjach if

2: #include <iostream>

3: int main()

4: {

5: int x;

6: std::cout << "Wpisz liczbe mniejsza niz 10 lub wieksza niz 100: ";

7: std::cin >> x;

8: std::cout << "\n";

9:

10: if (x >= 10)

11: {

12: if (x > 100)

13: std::cout << "Wieksza niz 100, Dzieki!\n";

14: }

15: else // poprawione!

16: std::cout << "Mniejsza niz 10, Dzieki!\n";

17: return 0;

18: }

Wynik

Wpisz liczbe mniejsza niz 10 lub wieksza niz 100: 9

Mniejsza niz 10, Dzieki!

Analiza

Nawiasy klamrowe w liniach 11. i 14. powodują, że cały zawarty między nimi kod jest traktowany jak pojedyncza instrukcja, dzięki czemu instrukcja else w linii 15. odnosi się teraz do instrukcji if w linii 10., czyli tak, jak zamierzono.

UWAGA Programy przedstawione w tej książce zostały napisane w celu zilustrowania omawianych zagadnień. Są więc z założenia uproszczone i nie zawierają żadnych mechanizmów kontroli błędów wpisywanych przez użytkownika danych. W profesjonalnym kodzie należy przewidzieć każdy błąd i odpowiednio na niego zareagować.

Operatory logiczne

Często zdarza się, że chcemy zadać więcej niż jedno relacyjne pytanie na raz. „Czy jest prawdą że x jest większe od y i czy jest jednocześnie prawdą, że y jest większe od z?” Aby móc podjąć działanie, program musi mieć możliwość sprawdzenia, czy oba te warunki są prawdziwe — lub czy przynajmniej któryś z nich jest prawdziwy.

Wyobraźmy sobie skomplikowany system alarmowy, działający zgodnie z następującą zasadą: „gdy zabrzmi alarm przy drzwiach I jest już po szóstej po południu I NIE ma świąt LUB jest weekend, wtedy zadzwoń po policję”. Do tego rodzaju obliczeń stosowane są trzy operatory logiczne języka C++. Zostały one przedstawione w tabeli 4.2.

Tabela 4.2. Operatory logiczne

Operator

Symbol

Przykład

I (AND)

&&

wyrażenie1 && wyrażenie2

LUB (OR)

||

wyrażenie1 || wyrażenie2

NIE (NOT)

!

!wyrażenie

Logiczne I

Instrukcja logicznego I (AND) oblicza dwa wyrażenia, jeżeli oba mają wartość true, wartością całego wyrażenia I także jest true. Jeśli prawdą jest, że jesteś głodny I prawdą jest, że masz pieniądze, WTEDY możesz kupić obiad. Zatem

if ( (x == 5) && (y == 5) )

będzie prawdziwe, gdy zarówno x, jak i y ma wartość 5, zaś będzie nieprawdziwe, gdy któraś z tych zmiennych będzie miała wartość różną od 5. Zapamiętaj, że aby całe wyrażenie było prawdziwe, prawdziwe muszą być oba wyrażenia.

Zauważ, że logiczne I to podwójny symbol, &&. Pojedynczy symbol, &, jest zupełnie innym operatorem, który opiszemy w rozdziale 21., „Co dalej.”

Logiczne LUB

Instrukcja logicznego LUB (OR) oblicza dwa wyrażenia, gdy któreś z nich ma wartość true, wtedy wartością całego wyrażenia LUB także jest true. Jeśli prawdą jest, że masz gotówkę LUB prawdą jest że, masz kartę kredytową, WTEDY możesz zapłacić rachunek. Nie potrzebujesz jednocześnie gotówki i karty kredytowej, choć posiadanie obu jednocześnie nie przeszkadza. Zatem

if ( (x == 5) || (y == 5) )

będzie prawdziwe, gdy x lub y ma wartość 5, lub gdy obie zmienne mają wartość 5.

Zauważ, że logiczne LUB to podwójny symbol,[Author ID1: at Sat Oct 20 14:04:00 2001 ] ||. Pojedynczy symbol,[Author ID1: at Sat Oct 20 14:04:00 2001 ] |,[Author ID1: at Sat Oct 20 14:04:00 2001 ] jest zupełnie innym operatorem, który opiszemy w rozdziale 21., „Co dalej.”

Logiczne NIE

Instrukcja logicznego NIE (NOT) ma wartość true, gdy sprawdzane wyrażenie ma wartość false. Jeżeli sprawdzane wyrażenie ma wartość true, operator logiczny[Author ID1: at Sat Oct 20 14:05:00 2001 ]ego[Author ID1: at Sat Oct 20 14:05:00 2001 ] NIE zwraca wartość false. Zatem

if ( !(x == 5) )

jest prawdziwe tylko wtedy, gdy x jest różne od 5. Identycznie działa zapis:

if (x != 5)

Skrócone obliczanie wyrażeń logicznych

Gdy kompilator oblicza instrukcję I, na przykład taką, jak:

if ( (x == 5) && (y == 5) )

wtedy najpierw sprawdza prawdziwość pierwszego wyrażenia (x == 5). Gdy jest ono nieprawdziwe, POMIJA sprawdzanie prawdziwości drugiego wyrażenia (y == 5), gdyż instrukcja I wymaga, aby oba wyrażenia były prawdziwe.

Gdy kompilator oblicza instrukcję LUB, na przykład taką, jak:

if ( (x == 5) || (y == 5) )

wtedy w przypadku prawdziwości pierwszego wyrażenia (x == 5), nigdy NIE JEST sprawdzane drugie wyrażenie (y == 5), gdyż w instrukcji LUB wystarczy prawdziwość któregokolwiek z wyrażeń.

Kolejność operatorów logicznych

Operatory logiczne, podobnie jak operatory relacji, są w języku C++ wyrażeniami, więc zwracają wartości; w tym przypadku wartość true lub false. Tak jak wszystkie wyrażenia, posiadają priorytet (patrz dodatek C), określający kolejność ich obliczania. Ma on znaczenie podczas wyznaczania wartości instrukcji

if ( x > 5 && y > 5 || z > 5)

Być może programista chciał, by to wyrażenie miało[Author ID1: at Sat Oct 20 14:07:00 2001 ] wartość true, gdy zarówno x, jak i y są większe od 5 lub gdy z jest większe od 5. Z drugiej strony, programista mógł chcieć, by to wyrażenie było prawdziwe tylko wtedy, gdy x jest większe od 5 i gdy y lub z jest większe od 5.

Jeśli x ma wartość 3, zaś y i z mają wartość 10, wtedy prawdziwa jest pierwsza interpretacja (z jest większe od 5, więc x i y są ignorowane). Jednak w drugiej interpretacji otrzymujemy wartość false (x nie jest większe od 5, więc nie ma znaczenia, co jest po prawej stronie symbolu &&, gdyż obie jego strony muszą być prawdziwe).

Choć o kolejności obliczeń decydują priorytety operatorów, jednak do zmiany ich kolejności i jasnego wyrażenia naszych zamiarów możemy użyć nawiasów:

if ( (x > 5) && (y > 5 || z > 5) )

Używając poprzednio opisanych wartości otrzymujemy dla tego wyrażenia wartość false. Ponieważ x nie jest większe od 5, lewa strona instrukcji I jest nieprawdziwa, więc całe wyrażenie jest traktowane jako nieprawdziwe. Pamiętaj, że instrukcja I wymaga, by obie strony były prawdziwe.

UWAGA Dobrym pomysłem jest używanie dodatkowych nawiasów - pomagają one lepiej oznaczyć operatory, które chcesz pogrupować. Pamiętaj, że twoim celem jest pisanie programów, które nie tylko działają, ale są także łatwe do odczytania i zrozumienia.

Kilka słów na temat prawdy i fałszu

W języku C++ wartość zero jest traktowana jako logiczna wartość false, zaś wszystkie inne wartości są traktowane jako logiczna wartość true. Ponieważ wyrażenie zawsze posiada jakąś wartość, wielu programistów wykorzystuje ją w swoich instrukcjach if. Instrukcja taka jak

if(x) // jeśli x ma wartość true (różną od zera)

x = 0;

może być odczytywana jako “jeśli x ma wartość różną od zera, ustaw x na 0”. Jest to efektowna sztuczka; zamiast tego lepiej będzie, gdy napiszesz:

if (x != 0) // jeśli x ma wartość różną od zera

x = 0;

Obie instrukcje są dozwolone, ale druga z nich lepiej wyraża intencje programisty. Do dobrych obyczajów programistów należy pozostawienie pierwszej z form dla prawdziwych testów logicznych (a nie dla sprawdzania czy wartość jest różna od zera).

Te dwie instrukcje także są równoważne:

if (!x) // jeśli x ma wartość false (równą zeru)

if (x == 0) // jeśli x ma wartość zero

Druga z nich jest nieco łatwiejsza do zrozumienia i wyraźniej sugeruje, że sprawdzamy matematyczną wartość zmiennej x, a nie jej stan logiczny.

TAK

NIE

Aby lepiej wyrazić kolejność obliczeń, umieszczaj nawiasy wokół wyrażeń logicznych.

Aby uniknąć błędów i lepiej wyrazić przynależność instrukcji else, używaj nawiasów klamrowych w zagnieżdżonych instrukcjach if.

Nie używaj if(x) jako synonimu dla if(x != 0); druga z tych form jest bardziej czytelna.

Nie używaj if(!x) jako synonimu dla if(x == 0); druga z tych form jest bardziej czytelna.

Operator warunkowy (trójelementowy)

Operator warunkowy (?:) jest w języku C++ jedynym operatorem trójelementowym, tj. operatorem korzystającym z trzech wyrażeń.

Operator warunkowy składa się z trzech wyrażeń i zwraca wartość:

(wyrażenie1) ? (wyrażenie2) : (wyrażenie3)

Tę linię odczytuje się jako: „jeśli wyrażenie1 jest prawdziwe, zwróć wartość wyrażenia2; w przeciwnym razie zwróć wartość wyrażenia3”. Zwracana wartość jest zwykle przypisywana zmiennej.

Listing 4.9 przedstawia instrukcję if przepisaną z użyciem operatora warunkowego.

Listing 4.9. Przykład użycia operatora warunkowego

0: // Listing 4.9 - demonstruje operator warunkowy

1: //

2: #include <iostream>

3: int main()

4: {

5: using namespace std;

6:

7: int x, y, z;

8: cout << "Wpisz dwie liczby.\n";

9: cout << "Pierwsza: ";

10: cin >> x;

11: cout << "\nDruga: ";

12: cin >> y;

13: cout << "\n";

14:

15: if (x > y)

16: z = x;

17: else

18: z = y;

19:

20: cout << "z: " << z;

21: cout << "\n";

22:

23: z = (x > y) ? x : y;

24:

25: cout << "z: " << z;

26: cout << "\n";

27: return 0;

28: }

Wynik

Wpisz dwie liczby.

Pierwsza: 5

Druga: 8

z: 8

z: 8

Analiza

Tworzone są trzy zmienne całkowite: x, y oraz z. Wartości dwóch pierwszych są nadawane przez użytkownika. Instrukcja if w linii 15. sprawdza, która wartość jest większa i przypisuje ją zmiennej z. Ta wartość jest wypisywana w linii 20.

Operator warunkowy w linii 23. przeprowadza ten sam test i przypisuje zmiennej z większą z wartości. Można go odczytać jako: jeśli x jest większe od y, zwróć wartość x; w przeciwnym razie zwróć wartość y”. Zwracana wartość jest przypisywana zmiennej z, zaś jej wartość jest wypisywana w linii 25. Jak widać, instrukcja warunkowa stanowi krótszy odpowiednik instrukcji if...else.

2 Część I Podstawy obsługi systemu WhizBang (Nagłówek strony)

2 F:\korekta\r04-06.doc



Wyszukiwarka