1_3.pdf

(470 KB) Pobierz
Od zera do gier kodera
3
DZIAŁANIE PROGRAMU
Nic nie dzieje się wbrew naturze,
lecz wbrew temu, co o niej wiemy.
Fox Mulder w serialu „Z archiwum X”
Poznaliśmy już przedsmak uroków programowania w C++ i stworzyliśmy kilka własnych
programów. Opanowaliśmy jednocześnie najbardziej podstawowe podstawy podstaw
kodowania w tym języku :)
Możemy więc odkryć kolejne, ważne jego elementy, które pozwolą nam tworzyć bardziej
interesujące i przydatne programy. Przyszła bowiem pora na spotkanie z instrukcjami
sterującymi przebiegiem aplikacji i sposobem jej działania.
Funkcje nieco bliżej
Z funkcjami mieliśmy do czynienia już wcześniej. Powiedzieliśmy sobie wtedy, że są to
wydzielone fragmenty kodu realizujące jakąś czynność. Przytoczyłem przy tym dość
banalny przykład funkcji PokazTekst() , wyświetlającej w konsoli ustalony napis.
Niewątpliwie był on klarowny i ukazywał wspomniane tam przeznaczenie funkcji (czyli
podział kodu na fragmenty), jednakże pomijał dwa bardzo ważne aspekty z nimi
związane. Chodzi mianowicie o parametry oraz zwracanie wartości . Rozszerzają one
użyteczność i możliwości funkcji tak znacznie, że bez nich w zasadzie trudno wyobrazić
sobie skuteczne i efektywne programowanie.
W takiej sytuacji nie możemy wszak przejść obok nich obojętnie - niezwłocznie
przystąpimy zatem do poznawania tych arcyważnych zagadnień :)
Parametry funkcji
Nie tylko w programowaniu trudno wskazać operację, którą można wykonać bez
posiadania o niej dodatkowych informacji. Przykładowo, nie można wykonać operacji
kopiowania czy przesunięcia pliku do innego katalogu, jeśli nie jest znana nazwa tegoż
pliku oraz nazwa docelowego folderu.
Gdybyśmy napisali funkcję realizującą taką czynność, to nazwy pliku oraz katalogu
finalnego byłyby jej parametrami .
Parametry funkcji to dodatkowe dane, przekazywane do funkcji podczas jej wywołania.
Parametry pełnią rolę dodatkowych zmiennych wewnątrz funkcji i można ich używać
podobnie jak innych zmiennych, zadeklarowanych w niej bezpośrednio. Różnią się one
oczywiście tym, że wartości parametrów pochodzą z „zewnątrz” – są im przypisywane
podczas wywołania funkcji.
262818418.012.png
78
Podstawy programowania
Po tym krótkim opisie czas na obrazowy przykład. Oto zmodyfikujemy nasz program
liczący tak, żeby korzystał z dobrodziejstw parametrów funkcji:
// Parameters - wykorzystanie parametrów funkcji
#include <iostream>
#include <conio.h>
void Dodaj( int nWartosc1, int nWartosc2)
{
int nWynik = nWartosc1 + nWartosc2;
std::cout << nWartosc1 << " + " << nWartosc2 << " = " << nWynik;
std::cout << std::endl;
}
void main()
{
int nLiczba1;
std::cout<< "Podaj pierwsza liczbe: " ;
std::cin >> nLiczba1;
int nLiczba2;
std::cout<< "Podaj druga liczbe: " ;
std::cin >> nLiczba2;
Dodaj (nLiczba1, nLiczba2);
getch();
}
Rzut oka na działający program pozwala stwierdzić, iż wykonuje on taką samą pracę, jak
jego poprzednia wersja. Z kolei spojrzenie na kod ujawnia w nim widoczne zmiany –
przyjrzyjmy się im.
Zasadnicza czynność programu, czyli dodawanie dwóch liczb, została wyodrębniona w
postaci osobnej funkcji Dodaj() . Posiada ona dwa parametry nWartosc1 i nWartosc2 ,
które są w niej dodawane do siebie i wyświetlane w konsoli.
Wielce interesujący jest w związku z tym nagłówek funkcji Dodaj() , zawierający
deklarację owych dwóch parametrów:
void Dodaj( int nWartosc1, int nWartosc2)
Jak sam widzisz, wygląda ona bardzo podobnie do deklaracji zmiennych – najpierw
piszemy typ parametru, a następnie jego nazwę. Nazwa ta pozwala odwoływać się do
wartości parametru w kodzie funkcji, a więc na przykład użyć jej jako składnika sumy.
Określenia kolejnych parametrów oddzielamy od siebie przecinkami, zaś całą deklarację
umieszczamy w nawiasie po nazwie funkcji.
Wywołanie takiej funkcji jest raczej oczywiste:
Dodaj (nLiczba1, nLiczba2);
Podajemy tu w nawiasie kolejne wartości , które zostaną przypisane jej parametrom;
oddzielamy je tradycyjnie już przecinkami. W niniejszym przypadku parametrowi
nWartosc1 zostanie nadana wartość zmiennej nLiczba1 , zaś nWartosc2 – nLiczba2 .
Myślę, że jest to dość intuicyjne i nie wymaga więcej wyczerpującego komentarza :)
***
Działanie programu
79
Reasumując: parametry pozwalają nam przekazywać funkcjom dodatkowe dane, których
mogą użyć do wykonania swoich działań. Ilość, typy i nazwy parametrów deklarujemy w
nagłówku funkcji, zaś podczas jej wywoływania podajemy ich wartości w analogiczny
sposób.
Wartość zwracana przez funkcję
Spora część funkcji pisanych przez programistów ma za zadanie obliczenie jakiegoś
wyniku (często na podstawie przekazanych im parametrów). Inne z kolei wykonują
operacje, które nie zawsze muszą się udać (choćby usunięcie pliku – dany plik może
przecież już nie istnieć).
W takich przypadkach istnieje więc potrzeba, by funkcja zwróciła jakąś wartość .
Niekiedy będzie to rezultat jej intensywnej pracy, a innym razem jedynie informacja, czy
zlecona funkcji czynność została wykonana pomyślnie.
Zgodnie ze zwyczajem, popatrzymy teraz na odpowiedni program przykładowy 17 : ) Pyta
on użytkownika o długości boków prostokąta, wyświetlając w zamian jego pole
powierzchni oraz obwód. Czyni to ten kod (jest to fragment funkcji main() ):
// ReturnValue - funkcje zwracające wartość
int nDlugosc1;
std::cout << "Podaj dlugosc pierwszego boku: " ;
std::cin >> nDlugosc1;
int nDlugosc2;
std::cout << "Podaj dlugosc drugiego boku: " ;
std::cin >> nDlugosc2;
std::cout << "Obwod prostokata: " << Obwod(nDlugosc1, nDlugosc2) <<
std::endl;
std::cout << "Pole prostokata: " << Pole(nDlugosc1, nDlugosc2) <<
std::endl;
getch();
Linijki wyświetlające gotowy wynik zawierają wywołania dwóch funkcji – Obwod() i
Pole() . Można je umieścić w tym miejscu, gdyż zwracają one wartości – w dodatku te,
które chcemy zaprezentować :) Wyświetlamy je dokładnie w ten sam sposób jak wartości
zmiennych i wszystkich innych wyrażeń liczbowych.
Cała istota działania aplikacji zawiera się więc w tych dwóch funkcjach. Prezentują się
one następująco:
int Obwod( int nBok1, int nBok2)
{
return 2 * (nBok1 + nBok2);
}
int Pole( int nBok1, int nBok2)
{
return nBok1 * nBok2;
}
Cóż ciekawego możemy o nich rzec? Widoczną różnicą w stosunku do dotychczasowych
przykładów funkcji, jakie mieliśmy okazję napotkać, jest chociażby zastąpienie słówka
17 Całość programu jest dołączona do tutoriala, tutaj zaprezentujemy tylko jego najważniejsze fragmenty
262818418.013.png
 
80
Podstawy programowania
void przez nazwę typu, int . To oczywiście nie przypadek – w taki właśnie sposób
informujemy kompilator, iż nasza funkcja ma zwracać wartość oraz wskazujemy jej typ.
Kod funkcji to tylko jeden wiersz, zaczynający się od return (‘powrót’). Określa on ni
mniej, ni więcej, jak tylko ową wartość, która będzie zwrócona i stanie się wynikiem
działania funkcji. Rezultat ten zobaczymy w końcu i my w oknie konsoli:
Screen 15. Miernik prostokątów w akcji
return powoduje jeszcze jeden efekt, który nie jest tu tak wyraźnie widoczny. Użycie tej
instrukcji skutkuje mianowicie natychmiastowym przerwaniem działania funkcji i
powrotem do miejsca jej wywołania. Wprawdzie nasze proste funkcje i tak kończą się
niemal od razu, więc nie ma to większego znaczenia, jednak w przypadku poważniejszych
podprogramów należy o tym fakcie pamiętać.
Wynika z niego także możliwość użycia return w funkcjach niezwracających żadnej
wartości – można je w ten sposób przerwać, zanim wykonają swój kod w całości.
Ponieważ nie mamy wtedy żadnej wartości do zwracania, używamy samego słowa
return ; - bez wskazywania nim jakiegoś wyrażenia.
***
Dowiedzieliśmy się zatem, iż funkcja może zwracać wartość jako wynik swej pracy.
Rezultat taki winien być określonego typu – deklarujemy go w nagłówku funkcji jeszcze
przed jej nazwą. Natomiast w kodzie funkcji możemy użyć instrukcji return , by wskazać
wartość będącą jej wynikiem. Jednocześnie instrukcja ta spowoduje zakończenie
działania funkcji.
Składnia funkcji
Gdy już poznaliśmy zawiłości i niuanse związane z używaniem funkcji w swoich
aplikacjach, możemy tą wydatną porcję informacji podsumować ogólnymi regułami
składniowymi dla podprogramów w C++. Otóż postać funkcji w tym języku jest
następująca:
typ_zwracanej_wartości / void nazwa_funkcji ( [ typ_parametru nazwa , ... ] )
{
instrukcje_1
return wartość_funkcji_1 ;
instrukcje_2
return wartość_funkcji_2 ;
instrukcje_3
return wartość_funkcji_3 ;
...
return wartość_funkcji_n ;
}
Jeżeli dokładnie przestudiowałeś (i zrozumiałeś! :)) wiadomości z tego paragrafu i z
poprzedniego rozdziału, nie powinna być ona dla ciebie żadnym zaskoczeniem.
262818418.014.png 262818418.001.png 262818418.002.png 262818418.003.png 262818418.004.png 262818418.005.png 262818418.006.png 262818418.007.png 262818418.008.png 262818418.009.png 262818418.010.png
Działanie programu
81
Zauważ jeszcze, ze fraza return może występować kilka razy, zwracać w każdym z
wariantów różne wartości, a całość funkcji mieć logiczny sens i działać poprawnie. Jest to
możliwe między innymi dzięki instrukcjom warunkowym, które poznamy już za chwilę.
***
W ten oto sposób uzyskaliśmy bardzo ważną umiejętność programistyczną, jaką jest
poprawne używanie funkcji we własnych programach. Upewnij się przeto, iż skrupulatnie
przyswoiłeś sobie informacje o tym zagadnieniu, jakie serwowałem w aktualnym i
poprzednim rozdziale. W dalszej części kursu będziemy często korzystać z funkcji w
przykładowych kodach i omawianych tematach, dlatego ważne jest, byś nie miał
kłopotów z nimi.
Jeśli natomiast czujesz się na siłach i chcesz dowiedzieć czegoś więcej o funkcjach,
zajrzyj do pomocy MSDN zawartej w Visual Studio.
Sterowanie warunkowe
Dobrze napisany program powinien być przygotowany na każdą ewentualność i
nietypową sytuację, jaka może się przydarzyć w czasie jego działania. W niektórych
przypadkach nawet proste czynności mogą potencjalnie kończyć się niepowodzeniem, zaś
porządna aplikacja musi radzić sobie z takimi drobnymi (lub całkiem sporymi) kryzysami.
Oczywiście program nie uczyni nic, czego nie przewidziałby jego twórca. Dlatego też
ważnym zadaniem programisty jest opracowanie kodu reagującego odpowiednio na
nietypowe sytuacje, w rodzaju błędnych danych wprowadzonych przez użytkownika lub
braku pliku potrzebnego aplikacji do działania.
Możliwe są też przypadki, w których dla kilku całkowicie poprawnych sytuacji, danych itp.
trzeba wykonać zupełnie inne operacje. Ważne jest wtedy rozróżnienie tych wszystkich
wariantów i skierowanie działania programu na właściwe tory, gdy ma miejsce któryś z
nich.
Do wszystkich tych zadań stworzono w C++ (i w każdym języku programowania) zestaw
odpowiednich narzędzi, zwanych instrukcjami warunkowymi . Ich przeznaczeniem jest
właśnie dokonywanie różnorakich wyborów, zależnych od ustalonych warunków .
Jak widać, przydatność tych konstrukcji jest nadspodziewanie duża, żal byłoby więc
ominąć je bez wnikania w ich szczegóły, prawda? :) Niezwłocznie zatem zajmiemy się
nimi, poznając ich składnię i sposób funkcjonowania.
Instrukcja warunkowa if
Instrukcja if (‘jeżeli’) pozwala wykonać jakiś kod tylko wtedy, gdy spełniony jest
określony warunek. Jej działanie sprowadza się więc do sprawdzenia tegoż warunku i,
jeśli zostanie stwierdzona jego prawdziwość, wykonania wskazanego bloku kodu.
Tę prostą ideę może ilustrować choćby taki przykład:
// SimpleIf – prosty przykład instrukcji if
void main()
{
int nLiczba;
std::cout<< "Wprowadz liczbe wieksza od 10: " ;
std::cin >> nLiczba;
262818418.011.png
Zgłoś jeśli naruszono regulamin