Nader często zachodzi konieczność zbadania jakiegoś zdarzenia występującego w programie, a po tym do odpowiedniej reakcji na nie. Prowadzi to do rozwidleń w działaniu programu, gdzie w zależności od wyników badania danego zdarzenia wykonujemy inny kod programu, a następnie po obsłużeniu tego zdarzenia program realizuje się dalej.

If () else…

Tego typu obsługa nazywa się obsługą poprzez wyrażenia warunkowe (ang: conditional statements). Najprostszym takim mechanizmem w C/C++ jest konstrukcja:

if (warunek) { …kod programu jeśli prawda… } else { …kod programu jeśli fałsz… }

warunek jest wyrażeniem, którego stan logiczny jest obliczany i określany jako prawda lub fałsz. Wyrażenie warunku jest zapisem arytmetycznym.
kod programu jeśli prawda jest blokiem programowym ograniczonym klamrami { } (lub bez jeśli kodem jest pojedynczy rozkaz) wykonywanym tylko w przypadku, kiedy wynik obliczonego warunku jest true.
kod programu jeśli fałsz jest blokiem programowym ograniczonym klamrami { } (lub bez jeśli kodem jest pojedynczy rozkaz) wykonywanym tylko w przypadku, kiedy wynik obliczonego warunku jest false. Ta struktura rozpoczyna się po słowie kluczowym else i nie jest obligatoryjna – co znaczy, że jeśli nie chcemy wprowadzać kodu wykonywanego w przypadku stwierdzenia false wartości warunku, to nie musimy używać tej struktury i wyrażenie warunkowe if kończy się wraz z klamrą zamykającą kod wykonywany w przypadku stwierdzenia true warunku.

Wyrażeniami warunku mogą być wyniki operacji, bądź postawione pytania w stylu np. “czy a jest mniejsze od b?” zapisywane jako: ( a < b ).

W sytuacji, gdy chcemy sprawdzić, czy dana wartość jest równa innej należy pamiętać, że nie wolno(!) nam użyć operatora przypisania =, a musimy(!) zamiast tego użyć operatora porównania ==.

Porównywanie równości/tożsamości dwu wyrażeń jest szerszym zagadnieniem i będzie jeszcze poruszane w późniejszym czasie przy okazji omawiania złożonych struktur danych.

Operatorami porównania są:

a == bczy a jest równe b
a != bczy a nie jest równe b
a < bczy a jest mniejsze od b
a<=bczy a jest mniejsze
lub równe b
a > bczy a jest większe od b
a>=bczy a jest większe
lub równe b

Poniżej kilka przykładów wyrażenia warunkowego:

int a=10;
int b=20;

if (a<b) cout << "a jest mniejsze od b";

if (a<b) cout << "a jest mniejsze od b"; else "a nie jest mniejsze od b";

if (a<b) cout << "a jest mniejsze od b"; else if (a==b) cout << "a jest równe b"; else "a nie jest mniejsze od b"; //<= drugie else dotyczy drugiego if!!!

if (a==b) cout << "a jest równe b"; else "a nie jest równe b";

if (a!=b) cout << "a nie jest równe b"; 

//UWAGA
if (a<b) if (a==b) cout << "a jest równe b"; else "a nie jest mniejsze od b"; //<= takie użycie nie ma sensu,  program się prawidłowo skompiluje, ale bowiem gdy prawdą jest, że a<b, to a nie ma sensu badanie czy a==b, bowiem nie mogą być równe - więc jest to błąd programisty w przemyśleniu struktury warunków!!!


// UWAGA!!! na poniższy przykład!!!
printf("Zmienna a=%d, zmienna b=%d \n", a, b);
if (a=b) cout << "Przypisanie wartości ze zmiennej b do zmiennej a udało się!";
printf("Zmienna a=%d, zmienna b=%d \n", a, b);

Ostatni przykład jest szczególny, poprzez użycie w warunku operatora przypisania =, a nie porównania ==.
Jeśli takie użycie nie jest zamierzone przez programistę, to najczęściej jest wynikiem błędu, czasem trudnego do wychwycenia, bowiem program kompiluje się poprawnie, tylko wynik jego działania daleki jest od oczekiwanego!

Wynikiem działania tego wyrażenia warunkowego jest:
– przypisanie do zmiennej a wartości ze zmiennej b (!)
– sprawdzenie, czy operacja wykonała się poprawnie (!)

Dla lepszego zrozumienia tego przypadku zostały wyświetlone wartości zmiennych ab przed wykonaniem ostatniej operacji if, oraz po niej – należy zwrócić uwagę jak zmieniły się one i dlaczego się to stało.
Wynika z tego informacja, że gdy nie możemy rozwinąć operacji porównania w celu określenia wartości logicznej takiej czynności, to i tak określana jest wartość operacji logicznej, która w takich przypadkach wynika z faktu, czy został przez wykonywany program zgłoszony błąd wykonania operacji (false), czy nie (true).

Wyrażenia warunkowe mogą być konstruowane jako złożenie wielu wyrażeń, a ich wynik może być konstrukcją wynikającą z operacji logicznych przeprowadzonych na nich. Tak więc operacjami logicznymi mogą być:

OperacjaOperatorZnaczenie
and
“i”
(koniunkcja)
&&rozwiązanie logiczne wszystkich
cząstkowych wyrażeń muszą
dać odpowiedź pozytywną
(dawać odpowiedź logiczną true)
or
“lub”
(alternatywa)
||jeden z wyników obliczonych
wyrażeń cząstkowych
musi być pozytywny (true)
not
“nie”
(negacja)
!negacja wyrażenia
musi dać
odpowiedź pozytywną (true)

Switch () case:

Innym rodzajem konstrukcji warunkowej jest konstrukcja wyboru switch/case. Polega ona na badaniu wartości (!) wyrażenia i w zależności od tej wartości skok do miejsca zadeklarowanego właściwą etykietą i rozpoczęcie przetwarzania bloku programu od tego miejsca.

Przykład jej użycia jest przedstawiony poniżej:

	int aa=20;
	switch (aa) {
		case 10 : cout << "Wartością zmiennej aa jest 10";
		case 20	: cout << "Wartością zmiennej aa jest 20";
		case 30 : cout << "Wartością zmiennej aa jest 30";
		default : cout << "Nie dopasowałem wartości aa";
	}	

Jak widać na powyższym przykładzie nie ma tutaj wprowadzonych porównań, są jedynie ściśle postawione wartości i w zależności od tego, czyt zmienna aa “wpasuje” się w nie następuje skok do odpowiedniej etykiety (pozycji case). W przypadku, gdy nie udaje się dopasować wyrażenia do żadnej z wymienionych wartości, można użyć etykiety default – wtedy w takiej sytuacji zostanie wykonany kod rozpoczynający się od tego miejsca w strukturze. Po przetestowaniu wymienionego wyżej programu zauważamy pewną nieoczekiwaną sytuacje – wyświetliły się wszystkie komunikaty od miejsca oznaczonego wyborem case 20. Wyjaśniając, nadmienić należy, że kod wykonuje się od miejsca do którego “wskoczył” dzięki porównanej wartości, aż do końca struktury, czyli realizowane są WSZYSTKIE następujące linie rozkazów. Nie zawsze (lub raczej rzadko kiedy) jest to celowe i zamierzone. Jeśli nie chcemy, aby realizowały się inne za wyjątkiem wybranych linii kodu, musimy przerwać dalsze przetwarzanie poleceniem break; – powoduje ono opuszczenie aktualnego zagnieżdżenia programu (tutaj całego bloku switch/case) i przejście do sekwencji instrukcji następujących po opuszczanej strukturze. Tak więc prawdopodobnie raczej wolelibyśmy użyć poniższej konstrukcji:

	int aa=20;
	switch (aa) {
		case 10 : cout << "Wartością zmiennej aa jest 10";
			  break;
		case 20	: cout << "Wartością zmiennej aa jest 20";
			  break;
		case 30 : cout << "Wartością zmiennej aa jest 30";
			  break;
		default : cout << "Nie dopasowałem wartości aa";
	}	

Można szybko zauważyć, że jest możliwa pewna niedogodność w stosowaniu switch/case, a mianowicie co z zakresami wartości zmiennych, kiedy chcielibyśmy wykonać dany kod w przypadku, gdy wartość poddana ewaluacji w switch jest z zakresu np. 10-19.
W standardzie C/C++ do standardu +11 włącznie nie ma możliwości użycia zakresów, jednakże z pomocą tutaj przyjdzie nam obecnie istniejący standard GNU C++ umożliwiający użycie zakresów. Przykładem jest poniższy kod:

	int aa=15;
	switch (aa) {
		case 10 ... 19 : cout << "aa jest z zakresu 10-19";
				 break;
		case 20	... 29 : cout << "aa jest z zakresu 20-29";
				 break;
		case 30 : cout << "Wartością zmiennej aa jest 30";
				break;
	}	

Ważną uwagą jest to aby wartości w etykietach skoku przy case były unikalne. Znaczy to, że zakresy nie mogą się wzajemnie przenikać i muszą być rozłączne.

Trójargumentowy operator wyboru ? :

Ciekawą konstrukcją jest trójargumentowy (ang: ternary operator) operator wyboru:

(wyrażenie warunkowe) ? blok gdy prawda : blok gdy fałsz ;

Działanie tej konstrukcji polega na badaniu wyrażenia warunkowego podanego przed znakiem ?, jeśli wynikiem jego ewaluacji jest prawda (true) to zostanie w miejsce kodu wstawiona sekwencja pomiędzy występującymi w konstrukcji znakami ? :, jeśli jednak wynikiem będzie fałsz (false) to zostanie wstawiona sekwencja występująca po znaku :.

Jego szczególną wersją jest tzw. Elvis Operator, gdy zapisujemy go tak:

(wyrażenie warunkowe) ?: blok gdy fałsz ;

Działanie tej konstrukcji polega na wstawieniu w miejsce kodu wartość wyrażenia warunkowego jeśli jest ono prawdą (true), albo sekwencji po ?: jeśli wynikiem ewaluacji wyrażenia warunkowego jest fałsz (false).

Przykłady użycia tych konstrukcji przedstawione są poniżej.

cout << ((a<b)?"Wartością mniejszą jest a":"Wartością mniejszą jest b");	

(a<b)?b-=a:a-=b;  // if (a<b) b=b-a; else a=a-b;
	
if ((a<b)?:a-=b) cout << " Wartość a została znormalizowana.";  // Elvis Operator: jeśli (a<b) to jest ok, ale jeśli nie to odejmij od a wartość b.

Zadania do samodzielnego wykonania: Należy przetestować wymienione w tym temacie konstrukcje i zapoznać się z mechanizmami wyborów warunkowych.

Zadanie 1: Napisz program, w którym zadeklarujesz i zainicjalizujesz w sposób dowolny wartościami 10 liczb, a następnie dokonasz ich porównania ze sobą w ten sposób, aby na ekranie wyświetliły się one w kolejności od najmniejszej do największej.