Strona 1
[lekcja] Funkcje - pierwsze starcie | Kurs C++ » Poziom 2
2011-11-12 17:06:50
http://cpp0x.pl/kursy/Kurs-C++/Poziom-2/Funkcje-pierwsze-starcie/291
Darmowe kursy Online
»
Kurs C++
»
Poziom 2
Autor: Piotr Szawdyński
Funkcje - pierwsze starcie
[lekcja]
Rozdział 17. Co to są funkcje i jak się z nich korzysta.
Korzystając z przypływu weny pisarskiej, a nie tej ścisło naukowej - wykorzystam ten dzień na napisanie kolejnego rozdziału niniejszego
kursu C++. W obecnym rozdziale omówione zostaną funkcje w zakresie podstawowym, dzięki którym będziesz mógł pisać kod lepszy ,
czytelniejszy i przede wszystkim łatwiejszy w utrzymaniu. Czy tak będzie w praktyce? To już zależy tylko od Ciebie, tj. od tego z jakim
zaangażowaniem do obecnie czytanego rozdziału podejdziesz .
Co to są funkcje i argumenty
Skoro wstęp już mamy za sobą wyjaśnijmy sobie na początek czym są funkcje w językach programowania takich jak C i C++. Funkcja
jest to fragment programu, któremu nadano nazwę i który możemy wykonać poprzez podanie jego nazwy oraz ewentualnych
argumentów (o ile istnieją). Argumentami są natomiast dane przekazywane do funkcji.
Budowa funkcji
Zobaczmy teraz jak w teorii zbudowana jest funkcja:
C/C++
typ_zwracanej_wartosci nazwa_funkcji
(
typ_argumentu_1 nazwa_argumentu_1
/*,...*/
,
typ_argumentu_n nazwa_argumentu_n
)
{
return
zwracana_wartosc
;
}
W kursie, który pisałem kilka lat temu, zawarłem następujące stwierdzenia:
Każda funkcja posiada trzy własności:
zwraca dane (lub nie jeśli tego nie chcemy);
posiada swoją nazwę;
może posiadać dowolną liczbę argumentów wejściowych (lub może nie mieć żadnego, jeśli tego nie chcemy).
Siłą rzeczy w tej kwestii nic się nie zmieniło od tamtego czasu, więc powyższe stwierdzenia są nadal jak najbardziej aktualne i właściwe.
Funkcja, która nie przyjmuje argumentówi nie zwraca wartości
Zanim przejdę do dalszej teorii - zatrzymajmy się w tym miejscu na chwilę i skupmy się na praktycznej części Twojej edukacji. Na
początek zajmijmy się funkcją, która nie zwraca żadnej wartości, ani nie przyjmuje żadnych argumentów:
C/C++
void
to_jest_moja_funkcja
()
{
//INFO: tu kod Twojej funkcji
}
Tak wygląda najprostsza funkcja w językach C i C++. Słowo kluczowe void informuje kompilator (jak również i programistę), że funkcja
nie zwraca żadnej wartości. Nazwą funkcji w tym przypadku jest to_jest_moja_funkcja. Funkcja nie przyjmuje żadnych argumentów,
bowiem wewnątrz zaokrąglonych nawiasów jest pusto. Wewnątrz klamr umieszczamy kod, który ma się wykonać w chwili gdy zostanie
wywołana funkcja. Jest to blok instrukcji, który zwie się ciałem funkcji.
Wywoływaniefunkcji
Wywoływanie funkcji jest bardzo proste - wystarczy wpisać jej nazwę i przekazać wartości do funkcji. Ogólna postać wywołania funkcji
wygląda następująco:
C/C++
nazwa_funkcji
(
wartosc_argumentu_1
/*,...*/
,
wartosc_argumentu_n
)
;
Jak widać używanie funkcji jest banalne.
Przykład funkcji, które nie przyjmują argumentówi nie zwracają wartości
Teoria, teorią ale zobaczmy jak to wygląda w praktyce.
C/C++
Strona 2
[lekcja] Funkcje - pierwsze starcie | Kurs C++ » Poziom 2
2011-11-12 17:06:50
http://cpp0x.pl/kursy/Kurs-C++/Poziom-2/Funkcje-pierwsze-starcie/291
#include <iostream>
void
moja_funkcja
()
{
std
::
cout
<<
"[1] - dodawanie"
<<
std
::
endl
;
std
::
cout
<<
"[2] - odejmowanie"
<<
std
::
endl
;
std
::
cout
<<
"[0] - wyjscie z programu"
<<
std
::
endl
;
}
void
dodawanie
()
{
std
::
cout
<<
"Jeszcze nie oprogramowano"
<<
std
::
endl
;
}
void
odejmowanie
()
{
dodawanie
()
;
}
int
main
()
{
std
::
cout
<<
"W programie sa dostepne nastepujace opcje:"
<<
std
::
endl
;
moja_funkcja
()
;
std
::
cout
<<
"Zycze przyjemnego korzystania z programu"
<<
std
::
endl
<<
std
::
endl
;
int
liczba
;
do
{
moja_funkcja
()
;
std
::
cin
>>
liczba
;
switch
(
liczba
)
{
case
1
:
dodawanie
()
;
break
;
case
2
:
odejmowanie
()
;
break
;
default
:
break
;
}
//switch
}
while
(
liczba
!=
0
)
;
return
0
;
}
Przeanalizuj na spokojnie powyższy program, przetestuj jego działanie i zastanów się czy aby na pewno wszystko rozumiesz i jest dla
Ciebie jasne w 100%. Jeżeli nie - zrób sobie krótką przerwę na herbatę, po czym wróć do ponownej analizy programu - krótkie przerwy
dobrze służą programistom.
Komentarzdo przykładu
Jak widać program jest już trochę dłuższy niż te, które do tej pory pojawiały się w przykładach. Musisz wiedzieć, że kod będzie bardzo
szybko rozrastał się w każdej pisanej przez Ciebie aplikacji. Funkcje należą do narzędzi, które umożliwiają Ci podzielenie kodu aplikacji
na mniejsze części. Tworzenie funkcji jest najpotężniejszą bronią w ręku początkującego programisty, które jest niestety przez nich
samych nie doceniane i z uporem maniaka omijane szerokim łukiem - a to błąd. Dzięki funkcjom możesz wydzielić funkcjonalność np.
odpowiedzialną za dodawanie i odejmowanie do osobnych bloków, które są po pierwsze nazwane, a po drugie krótsze, bowiem ciało
funkcji realizuje tylko fragment całego programu. Nazwa funkcji powinna mówić programiście za co funkcja jest odpowiedzialna - dzięki
takiemu podejściu wracając do kodu po miesiącu albo nawet podczas szukania błędu nie będziesz musiał skakać po całym kodzie, tylko
po fragmencie, który jest odpowiedzialny np. za dodawanie.
Ćwiczenie praktyczne
Dokończ wyżej przedstawiony program - popraw funkcje dodawanie i odejmowanie tak, aby pierwsza z nich sumowała dwie podane
liczby i wypisywała wynik, a druga odejmowała dwie liczby i wypisywała wynik. Po ukończeniu programu zastanów się jak by wyglądał
kod gdybyś nie skorzystał z funkcji. Zastanów się czy faktycznie łatwiej będzie znaleźć ewentualne błędy w kodzie tworzonym w oparciu
o funkcje.
Zmienne wewnątrz funkcji, czyli zasięg widzenia zmiennych
Jeżeli wykonałeś ćwiczenie praktyczne z niniejszego rozdziału to zapewne nieświadomy niczego utworzyłeś dwie zmienne wewnątrz
funkcji dodawanie oraz dwie zmienne wewnątrz funkcji odejmowanie. Jeżeli tego nie zrobiłeś to znaczy, że albo nie wykonałeś zadania
albo wykonałeś je źle, bowiem użyłeś zmiennych globalnych o których nie powinieneś nic wiedzieć. Jeżeli użyłeś zmiennych globalnych to
znaczy, że albo użyłeś Internetu w sposób niewłaściwy albo jakiś 'mądry' kolega doradził Ci najgorsze możliwe rozwiązanie ze zmiennymi
Strona 3
[lekcja] Funkcje - pierwsze starcie | Kurs C++ » Poziom 2
2011-11-12 17:06:50
http://cpp0x.pl/kursy/Kurs-C++/Poziom-2/Funkcje-pierwsze-starcie/291
globalnymi. Jeżeli sam wpadłeś na niepoprawne rozwiązanie - to da się wybaczyć, ale raczej jest to mało prawdopodobne jeżeli uczysz
się programowania od zera z niniejszego kursu. Przytoczmy najpierw poprawnie napisaną funkcję dodawanie:
C/C++
void
dodawanie
()
{
int
a
;
std
::
cin
>>
a
;
int
b
;
std
::
cin
>>
b
;
std
::
cout
<<
"Wynik dodawania "
<<
a
<<
" + "
<<
b
<<
" = "
<<
a
+
b
<<
std
::
endl
;
}
Na chwilę obecną nie jest istotny fakt, że nie zabezpieczyliśmy funkcji przed możliwością wpisania nieprawidłowych danych - ważny jest
tu fakt zadeklarowania zmiennych a oraz b typu int. Zmienne, które utworzyliśmy wewnątrz funkcji są zmiennymi lokalnymi. Oznacza
to, że nie są one widoczne poza funkcją i istnieją one tylko w obrębie danej funkcji. W praktyce oznacza to, że z funkcji głównej main
nie będziesz miał dostępu do zmiennych a i b. Próba uzyskania dostępu do zmiennej a z funkcji main zakończy się następującym
błędem:
In function 'int main()':|
error: 'a' was not declared in this scope|
Komunikat ten mówi:
W funkcji 'int main()': błąd: 'a' nie zostało zadeklarowane w zasięgu."
. Innymi słowy zmienna o wyszczególnionej
nazwie nie istnieje w ciele funkcji main. Linijka, która spowodowała błąd wyglądała następująco:
C/C++
a
=
123
;
Analogiczny błąd uzyskamy, gdy z funkcji dodawanie spróbujemy dostać się do zmiennej liczba, która występuje w ciele funkcji main.
Wniosek z tego płynie prosty: funkcje nie widzą swoich zmiennych nawzajem. Bardzo piękna własność funkcji, którą Ty jeszcze będziesz
nie raz przeklinał :)
Czas życia zmiennychw funkcjach
Zmienne, które zostały utworzone w funkcji są zmiennymi tymczasowymi. Zmienne te pojawiają się do użytku przy każdym wywołaniu
funkcji jak również znikają po jej opuszczeniu. Zmienne te nie trzymają stanu z poprzedniego wywołania - innymi słowy za każdym
razem po wejściu w funkcję musisz nadać wartości zmiennym na nowo.
Zachowanie zmiennych lokalnych można zmienić tak, by przy każdym wejściu w funkcję była to ta sama zmienna i zachowywała swoją
ostatnią wartość. Bardzo rzadko się to jednak stosuje i jako początkujący programista nie powinieneś tych technik używać - najpierw
musisz bowiem dobrze zrozumieć podstawy by móc efektywnie programować.
Komunikowaniesię między funkcjami
Skoro już wiemy, że zmienne funkcji są od siebie całkowicie odizolowane fajnie by było gdyby mimo wszystko funkcje mogły się ze sobą
jakoś wymieniać się informacjami. Pierwszą metodą są zmienne globalne - zło w czystej postaci - za to powinni palić na stosie
początkujących programistów :) Kiedyś niestety będę musiał napisać co to jest, jednak prędko to nie nastąpi, więc mam nadzieję, że
zdążysz się nauczyć programować poprawnie do tego czasu.
Zwracaniewartości przez funkcję
Drugą metodą jak już się domyślasz jest zwracanie wartości przez funkcję. Przykładem takiej funkcji była np. funkcja
»
C
♦
rand
. Funkcja
ta pełniła rolę czarnej skrzynki - w jakiś cudowny sposób wyliczyła wartość losową , a następnie zwróciła nam wynik, który mogliśmy
później dowolnie przetwarzać. Jak zerkniesz na budowę funkcji, która została wyżej opisana zobaczysz , że na samym początku znajduje
się w zapisie stwierdzenie typ_zwracanej_wartosci. Innymi słowy za ten zapis podstawiasz typ danych jaki ma zwrócić funkcja. Na
chwilę obecną niech to będzie liczba zmiennoprzecinkowa float. Przykład:
C/C++
float
dodawanie_inne
()
{
float
a
;
std
::
cin
>>
a
;
float
b
;
std
::
cin
>>
b
;
return
a
+
b
;
}
W powyższej funkcji występuje również słowo kluczowe return. Słowem kluczowym return ustawiamy wartość jaka ma zostać
zwrócona przez funkcję. Myślę, że niczego trudnego tutaj nie ma, więc możemy się teraz skupić na tym w jaki sposób odczytywać
zwracane wartości.
Odczytywaniewartości zwracanejprzez funkcję
Strona 4
[lekcja] Funkcje - pierwsze starcie | Kurs C++ » Poziom 2
2011-11-12 17:06:50
http://cpp0x.pl/kursy/Kurs-C++/Poziom-2/Funkcje-pierwsze-starcie/291
Funkcja zwraca wartość, czyli poniższe zapisy przekażą nam wartość do tego co będzie występowało po lewej stronie funkcji
(łopatologiczne wytłumaczenie, ale co tam):
C/C++
float
wynik
=
dodawanie_inne
()
;
wynik
=
dodawanie_inne
()
;
std
::
cout
<<
dodawanie_inne
()
;
dodawanie_inne
()
;
W ostatnim przypadku po lewej stronie funkcji nie ma nic - oznacza to tyle, że funkcja się wykona i zwróci wartość ale nie zostanie ona
nigdzie zapisana. W konsekwencji wynik obliczeń przepadnie - czy to będzie pożądane? To zależy od tego co funkcja będzie robiła. Jeżeli
funkcja załóżmy zwraca informację o tym czy funkcja się powiodła czy nie, a Ty niespecjalnie się tym przejmujesz zakładając, że zawsze
zadziała to i zwrot funkcji na nic Ci nie będzie potrzebny . Dobry programista jednak odnosi się z szacunkiem do informacji zwracanych
przez funkcje i zastanowi się chociaż czy przypadkiem nie powinien on oprogramować sytuacji wyjątkowych, takich jak np. błąd działania
funkcji. Wywody, wywodami no ale wróćmy do nauki. Mamy teraz następujący kod:
C/C++
#include <iostream>
float
dodawanie_inne
()
{
float
a
;
std
::
cin
>>
a
;
float
b
;
std
::
cin
>>
b
;
return
a
+
b
;
}
int
main
()
{
std
::
cout
<<
"Wprowadz dwie liczby: "
;
float
tu_bedzie_wynik
=
dodawanie_inne
()
;
std
::
cout
<<
"Wynik dodawania wynosi: "
<<
tu_bedzie_wynik
<<
std
::
endl
;
return
0
;
}
Jeżeli przeanalizujesz dobrze powyższy kod to zauważysz, że uzyskaliśmy teraz komunikację jednostronną między funkcjami. Funkcja
main
otrzymuje informacje zwrotną, od funkcji dodawanie_inne. Informacja, która została zwrócona została przygotowana przez funkcję
dodawanie_inne
i mówiąc potocznie 'wystawiona' do odczytania. Zauważmy jednak, że nadal nie możemy komunikować się w drugą
stronę, tj. przekazywać informacji z funkcji main do funkcji dodawanie_inne. Z pomocą przyjdą tutaj argumenty funkcji, które jeszcze
nie zostały omówione.
Przekazywaniewartości do funkcji poprzez argumenty
Trzecią techniką komunikacji między funkcjami jest zastosowanie argumentów funkcji. Argumenty umożliwiają komunikację w dwie
strony, jednak w tym rozdziale zajmiemy się tylko i wyłącznie komunikacją w jedną stronę, tj. przekazywaniu danych do funkcji.
Dlaczego? Bo tak :) Najlepszym uzasadnieniem, które powinno do Ciebie trafić jest fakt, że wiele początkujących osób totalnie nie
rozumie co to jest argument, jak działa i jakie mają znaczenie poszczególne znaczki. Dlatego też zaczniemy od podstaw, które trzeba
opanować by wiedzieć później po co się robi różne inne cuda o których z pewnością będę pisał w dalszej części kursu.
Powróćmy teraz na chwilę do teorii, która została przedstawiona na samym początku niniejszego rozdziału:
C/C++
typ_zwracanej_wartosci nazwa_funkcji
(
typ_argumentu_1 nazwa_argumentu_1
/*,...*/
,
typ_argumentu_n nazwa_argumentu_n
)
{
return
zwracana_wartosc
;
}
Jedynym zagadnieniem, które nie zostało omówione do tej pory są zapisy występujące pomiędzy nawiasami zaokrąglonymi. To co
znajduje się między nimi nazywamy argumentami. Każdy argument musi mieć określony swój typ. Dodatkowo po nazwie typu podaje się
również nazwę zmiennej po to by można było się dostać do argumentu naszej funkcji. Kolejne argumenty funkcji rodziela się
przecinkami. Zobaczmy teraz kolejną funkcję, która będzie wykonywała operację dodawania:
C/C++
int
dodawanie
(
int
a
,
int
b
)
{
return
a
+
b
;
}
Wywołanie tej funkcji natomiast będzie wyglądało tak:
C/C++
int
iWynik
=
dodawanie
(
123
,
456
)
;
std
::
cout
<<
"Wynik dodawania wynosi: "
<<
iWynik
<<
std
::
endl
;
Strona 5
[lekcja] Funkcje - pierwsze starcie | Kurs C++ » Poziom 2
2011-11-12 17:06:50
http://cpp0x.pl/kursy/Kurs-C++/Poziom-2/Funkcje-pierwsze-starcie/291
Architektserwisu: Piotr Szawdyński
© Wszelkieprawa zastrzeżone2005-2011
Powyższe zapisy spowodują, że:
Pierwszy argument funkcji (argument a) otrzyma wartość 123;
Drugi argument funkcji (argument b) otrzyma wartość 456;
Wynik funkcji dodawanie(...) zostanie zwrócony do zmiennej iWynik;
Wartość znajdująca się w zmiennej iWynik zostanie wypisana na ekran.
Choć trochę śmiesznie wygląda funkcja, która wykonuje tak prostą operację to mimo wszystko pokazuje ona w jaki sposób argumenty
funkcji działają i jak ustawiać im wartości.
Zadanie domowe
Napisz funkcję, która będzie wczytywała liczby ze standardowego wejścia i zwracała wczytaną wartość. Funkcja ma zagwarantować,
że zwracana liczba zawsze jest poprawna. Oznacza to, że funkcja będzie musiała pytać użytkownika o wprowadzenie liczby dopóki nie
poda poprawnej.
C/C++
#include <iostream>
int
wczytajLiczbe
()
{
//TODO: tu Twój kod
}
int
main
()
{
std
::
cout
<<
"Podaj liczbe: "
;
int
liczba
=
wczytajLiczbe
()
;
std
::
cout
<<
"Podales liczbe: "
<<
liczba
<<
std
::
endl
;
return
0
;
}
Zmodyfikuj kalkulator, który pisałeś w jednym ze wcześniejszych rozdziałów tak, aby korzystał on ze wcześniej napisanej funkcji
wczytajLiczbe
.
Napisz funkcję, która losuje liczbę z przedziału od 50 do 60 włącznie. Wywołaj funkcję kilka razy (wypisz wylosowane wartości na
ekran) w celu przetestowania czy działa ona poprawnie.
Napisz funkcję, która losuje liczbę z przedziału, który zostanie podany poprzez argumenty funkcji. Przetestuj funkcję w poniższy
sposób:
C/C++
#include <iostream>
#include <cstdlib>
#include <ctime>
//... tu Twój kod
int
main
()
{
srand
(
time
(
NULL
) )
;
int
start
;
std
::
cin
>>
start
;
int
stop
;
std
::
cin
>>
stop
;
int
ile
=
20
;
do
{
std
::
cout
<<
wylosuj
(
start
,
stop
) <<
std
::
endl
;
ile
--
;
}
while
(
ile
>
0
)
;
return
0
;
}
Wszystkieteksty są chronioneprawami autorskimi. Kopiowanielub rozpowszechnianietreści poza niniejszymserwisem jest
zabronione.
Powyższe ograniczenie nie dotyczy autora opracowania, któremu przysługuje prawo do rozpowszechnianiawłasnego tekstu wedle własnego uznania.