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