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

ozna│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: