LEKCJA 4 - STRUKTURY IF-ELSE, SWITCH-CASE I GOTO

Poznałeś już podstawowe typy zmiennych i niektóre sposoby ich użycia. Do omówienia pozostały nam jeszcze tablice i rekordy, ale ponieważ nie znasz jeszcze pętli, temat ten może wydać się dosyć nudny. Dlatego w tej lekcji zajmiemy się omówieniem instrukcji warunkowych i skoków.

Często zdarza się, że pisząc program należy rozważyć kilka możliwości. Na przykład, wykonując operację dzielenia, trzeba najpierw sprawdzić, czy liczba, przez którą dzielimy, nie jest przypadkiem równa zero. Gdyby okazało się, że jest, zamiast podzielić liczby należałoby wystosować odpowiedni komunikat, np.: "nie wolno dzielić przez zero!".

Podobną sytuację przedstawia sposób obliczania pierwiastków równania kwadratowego. Bo jeśli delta jest mniejsza od zera, to nie istnieją pierwiastki rzeczywiste! Na pierwszy rzut oka widać, że należy rozpatrzyć przynajmniej 3 różne możliwości (delta<0, delta=0, delta>0).

Wszystkie te możliwości można rozpatrzyć przy pomocy instrukcji if().

Zajmiemy się teraz pierwszym przykładem: sprawdzeniem czy liczby można przez siebie podzielić.

#include <stdio.h> 
#include <conio.h> 
main (void) 
{ 
int liczba1,liczba2; 
clrscr(); 

printf("Podaj pierwszą liczbę: \n"); 
scanf("%d",&liczba1); 

printf("Podaj drugą liczbę: \n"); 
scanf("%d",&liczba2); 

if (liczba2==0) 
printf("Nie wolno dzielić przez 0!\n"); 
else
printf("Wynik dzielenia: %d\n",liczba1/liczba2); 

getch(); 
return 0 ; 
} 

Po wykonaniu programu ekran może mieć zawartość:

Podaj pierwszą liczbę
12
Podaj drugą liczbę
3
Wynik dzielenia: 4

lub:

Podaj pierwszą liczbę
15
Podaj drugą liczbę
0
Nie wolno dzielić przez 0!  

Jak widać, struktura instrukcji if() ma postać:

if (warunek) instrukcja1; 
  else instrukcja2;

Znaczy to:
Jeżeli (warunek) jest spełniony, to wykonaj instrukcję1.
W innym przypadku (jeśli warunek ten nie jest spełniony), wykonaj instrukcję2.

Warunek logiczny to taki, na który można opdowiedzieć "tak" lub "nie". Warunek na to, czy jedna liczba jest większa od drugiej można zapisać następująco: if (liczba1<liczba2)

W powyższym programie mamy jednak do czynienia z przypadkiem porównywania dwóch liczb w sensie sprawdzenia czy są one sobie równe. Operację takiego porównania zapisujemy w postaci dwóch znaków '=': if (liczba2 == 0)

Jest bardzo ważne, aby umieć odróżnić operację przypisywania od operacji porównywania, zwłaszcza, że gdybyśmy w instrukcji if powyższego programu napisali: (liczba2=0) zamiast (liczba2==0), kompilator nie wskazałby tego jako błąd. Program uruchomiłby się bez problemu, ale nie działałby prawidłowo!

Po napisaniu warunku należy napisać, co program w danym przypadku powinien zrobić. Często się zdarza, że dla danego przypadku program powinien wykonać więcej niż jedną instrukcję. Wtedy wszystkie te instrukcje należy zgrupować poprzez zastosowanie nawiasów klamrowych:

if (warunek)
{ 
    instrukcja1;
    instrukcja2;
    instrukcja3; 
} 

Jeżeli o tym zapomnimy, po spełnieniu warunku zostanie wykonana tylko pierwsza instrukcja, a pozostałe zostaną wykonane niezależnie od spełnienia warunku. Warto też zapamiętać, że po napisaniu warunku nie stawiamy średnika (stawiamy go dopiero po napisaniu instrukcji do wykonania). Jeżeli i o tym zapomnimy, w momencie spełnienia określonego warunku program wykona tzw. pustą instrukcję, czyli po prostu nic nie zrobi.

Jak widać, strukturę z przykładu można pozbawić możliwości rozpatrywania drugiego przypadku (else...). Wtedy jednak cała ta nasza procedura zareaguje tylko w określonym (jednym) przypadku. Na przykład można sprawdzić warunek następująco:

if (liczba2!=0) 
  printf("Liczby można przez siebie podzielić");

Z reguły jednak rozpatruje się zawsze kilka przypadków, a nie jeden lub dwa. Jak to zrobić? Weźmy pod uwagę przypadek szukania pierwiastków dwumianu:

#include <stdio.h> 
#include <conio.h> 
main (void) 
{ 
float A,B,C,delta; 
clrscr(); 

printf("Podaj współczynnik A: \n"); 
scanf("%f",&A); 
printf("Podaj współczynnik B: \n"); 
scanf("%f",&B); 
printf("Podaj współczynnik C: \n"); 
scanf("%f",&C); 

delta=b*b-4*a*c; 

if (delta<0) 
printf("Nie istnieją pierwiaski rzeczywiste!\n"); 
if (delta==0) 
printf("Istnieje jeden pierwiastek rzeczywisty.\n"); 
if (delta>0) 
printf("Istnieją dwa pierwiaski rzeczywiste.\n"); 

getch(); 
return 0 ; 
} 

Przy tak napisanym kodzie program sprawdzi wszystkie trzy podane warunki, niezależnie od tego, czy poprzedni warunek został spełniony czy też nie. Gdyby jednak w stosownych miejsach umieścić instrukcję else, program sprawdziłby najpierw pierwszy warunek. Gdyby nie był on spełniony program sprawdziłby drugi warunek, a kiedy ten również nie byłby spełniony program sprawdziłby trzeci warunek. Wydaje się to właściwie bez różnicy, ale jeżeli warunków do sprawdzenia jest 50 i pierwszy z nich jest spełniony, to po co sprawdzać następne 49? To straszne marnotrawienie czasu. Poprawmy więc nasz program do postaci:

#include <stdio.h> 
#include <conio.h> 
main (void) 
{ 
float A,B,C,delta; 
clrscr(); 

printf("Podaj współczynnik A: \n"); 
scanf("%f",&A); 
printf("Podaj współczynnik B: \n"); 
scanf("%f",&B); 
printf("Podaj współczynnik C: \n"); 
scanf("%f",&C); 

delta=b*b-4*a*c; 

if (delta<0) 
printf("Nie istnieją pierwiaski rzeczywiste!\n");
else 
if (delta==0) 
printf("Istnieje jeden pierwiastek rzeczywisty.\n"); 
else
printf("Istnieją dwa pierwiaski rzeczywiste.\n"); 

getch(); 
return 0 ; 
} 

Jak widać, ostatnią instrukcję if można było usunąć, ponieważ nie istnieje więcej możliwych przypadków na znak delty niż trzy. Warunek logiczny nie musi być warunkiem pojedynczym. Można łączyć ze sobą kilka warunków w jedną całość zamiast dla każdego używać osobnej instrukcji if. Na przykład, zamiast pisać:

if (warunek1)
  if (warunek2)
    printf("komunikat");

można napisać:

if ((warunek1)&&(warunek2)) printf("komunikat");

W obu przypadkach komunikat zostanie wyświetlony, jeśli obydwa warunki zostaną spełnione, jednakże w drugim przypadku kod jest bardziej elastyczny. Operator && (logiczne I) można zmienić do postaci || (logiczne lub):

if ((warunek1)||(warunek2)) printf("komunikat");

Wtedy wystarczy, aby tylko jeden warunek był spełniony, aby program wypisał komunikat. Wyrażenia takie można łączyć w bardziej skomplikowane, jednakże całość zawsze należy ująć w okrągły nawias.


Podsumowując:

  1. Jeżeli do rozpatrzenia mamy kilka przypadków, stosujemy instrukcję warunkową if.
  2. Instrukcję zapisujemy: if (warunek) instrukcja1; else instrukcja2.
  3. Dla więcej niż jednej instrukcji należy zgrupować je za pomocą nawiasów klamrowych.
  4. W warunku logicznym instrukcji if zawsze stosujemy operator porównania (==), a nie przypisania (=).
  5. Jeśli jednocześnie powinno być sprawdzone kilka warunków, łączymy je za pomocą operatorów logicznych.


Jeżeli chcemy sprawdzić więcej warunków struktura if ... else ... może się bardzo skomplikować. W takim przypadku lepiej jest użyć instrukcji case(). Zakłada ona z góry, że sprawdzeniu ulegnie sporo warunków. Strukturę instrukcji case pokaże następny przykład:

#include <stdio.h> 
#include <conio.h> 
main (void) 
{ 
char znak; 
clrscr(); 

printf("Wciśnij cyfrę od 0 do 5\n"); 
scanf("%c",&znak); 
switch(znak)
{
case '0': pritntf("nacisnąłeś klawisz 0");break;
case '1': pritntf("nacisnąłeś klawisz 1");break;
case '2': pritntf("nacisnąłeś klawisz 2");break;
case '3': pritntf("nacisnąłeś klawisz 3");break;
case '4': pritntf("nacisnąłeś klawisz 4");break;
case '5': pritntf("nacisnąłeś klawisz 5");break;
default: pritntf("nacisnąłeś jakiś inny klawisz");
}

getch(); 
return 0 ; 
}  

Oczywiście pisanie reakcji na wciśnięty klawisz ma sens przy bardziej złożonych programach (np. bazach danych). Funkcja ta spełnia często rolę menu - w zależności od tego jaki klawisz naciśniemy, wykonywana jest określona część programu. Na przykład jeśli wciśniemy klawisz 1 dopiszemy dane do bazy, a jeśli naciśniemy klawisz 2 to wyświetlimy bazę na ekranie itp.

Struktura case jest bardzo prosta i w zasadzie nawet bardziej czytelna od if (dla dużej ilości warunków). Rozpoczynamy ją sółwkiem switch i w nawiasie piszemy nazwę zmiennej, której wartości będziemy rozpatrywać. Wszystkie możliwe przypadki ujmujemy w nawias klamrowy. Każdy przypadek rozpoczynamy od słowa case, następnie piszemy określoną dla tego przypadku wartość, a za tym stawiamy dwukropek. Dalej wypisujemy wszystko, co program ma zrobić w danym przypadku i jeżeli nie chcemy, aby program sprawdzał następne możliwości, kończymy to słowem break. Powoduje to wyjście z całej struktury switch-case i wykonywanie dalszych instrukcji w programie.

Podobnie jak else w przypadku instrukcji if, tak i tu możemy jednocześnie określić "wszystkie pozostałe przypadki". Służy do tego określenie default, po którym definiujemy, co program ma zrobić, jeżeli określone wcześniej przypadki nie zdarzyły się. Strukturę tą najlepiej pokazuje powyższy przykład, więc zamiast zagłębiać się w opis, proponuję przeanalizować dokładniej treść programu.


Podsumowując:

  1. Instrukcję if stosujemy dla mniejszej ilości warunków do sprawdzenia, lub dla bardziej skomplikowanych warunków (wtedy stosujemy operatory logiczne np. && lub ||).
  2. Instrukcję case stosujemy dla dużej ilości prostych warunków.
  3. Określenia else (w instrukcji if) i default (w instrukcji case) znaczą: "dla pozostałych przypadków"


Przy omawianiu instrukcji switch-case należy wspomnieć również o instrukcji skoku: goto. Jest to bardzo przydatna instrukcja przy skomplikowanych programach. Powróćmy do naszego przykładu z instrukcją case. Po naciśnięciu odpowiedniego klawisza zostaje wykonana odpowiednia instrukcja. A gdybyśmy chcieli, żeby wykonana została nie jedna czy dwie instrukcje, a 20? Można oczywiście wypisywać je jedna po drugiej, ale kod szybko straciłby na czytelności. Można się więc posłużyć instrukcją skoku, aby program na chwilę przeszedł do innego miejsca kodu, wykonał to co jest tam napisane i z powrotem powrócił do miejsca początkowego. Jak oznaczyć miejsce, do którego program ma wyskoczyć? Służą do tego etykiety. Zmodyfikujmy nasz przykład:

#include <stdio.h> 
#include <conio.h> 
main (void) 
{ 
char znak; 
clrscr(); 

jeszcze_raz://to jest etykieta o nazwie jeszcze_raz
printf("Wciśnij cyfrę od 0 do 5\n"); 
scanf("%c",&znak); 
switch(znak)
{
case '0': goto koniec; break;
case '1': goto jeden; break;
case '2': pritntf("nacisnąłeś klawisz 2"); goto koniec; break;
case '3': pritntf("nacisnąłeś klawisz 3"); goto koniec; break;
case '4': pritntf("nacisnąłeś klawisz 4"); goto koniec; break;
case '5': goto jeszcze_raz;break;
default: pritntf("nacisnąłeś jakiś inny klawisz");
}
jeden://to jest etykieta o nazwie jeden
instrukcja1;
instrukcja2;
instrukcja3;
instrukcja4;
...
instrukcja5;
instrukcja6;
instrukcja7;

koniec: //to jest etykieta o nazwie koniec 

getch(); 
return 0 ; 
} 

Działanie tego programu jest bardzo proste:
wciśnij klawisz => jaki klawisz został wciśnięty? =>
jeśli 0 => idź do etykiety 'koniec' i zacznij wykonywać wszystko od tego miejsca;
jeśli 1 => idź do etykiety 'jeden' i zacznij wykonywać wszystko od tego miejsca;
jeśli 2 => napisz komunikat a potem idź do etykiety 'koniec' i zacznij wykonywać wszystko od tego miejsca;
jeśli 3 => napisz komunikat a potem idź do etykiety 'koniec' i zacznij wykonywać wszystko od tego miejsca;
jeśli 4 => napisz komunikat a potem idź do etykiety 'koniec' i zacznij wykonywać wszystko od tego miejsca;
jeśli 5 => idź do etykiety 'jeszcze_raz' i zacznij wykonywać wszystko od tego miejsca;


Podsumowując:

  1. Instrukcja goto jest to instukcja skoku do pewnego miejsca w kodzie programu.
  2. Miejsce skoku należy oznaczyć odpowiednią etykietą.
  3. Za pomocą goto można robić pętle programowe lub szybko skończyć jego działanie.


Autorem Kursu C++ jest Anna Miedzianowska (http://annamiedzianowska.republika.pl/.


Baner reklamowy: