Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych Autor artykułu: mgr Jerzy Wałaszek |
©2014 mgr
Jerzy Wałaszek
|
typ_wyniku nazwa(lista_parametrów)
{
treść;
return wynik;
}
Typ wyniku określa typ zwracanych przez funkcję danych. Może to być dowolny z poznanych typów (int, unsigned, double) lub void. Ten ostatni oznacza funkcję, która nie zwraca żadnego wyniku. W funkcji typu void nie musi wystąpić rozkaz return, a jeśli występuje, to jest bez parametru wynik. W takim przypadku rozkaz return oznacza po prostu zakończenie działania funkcji i powrót do miejsca wywołania.
Nazwa funkcji tworzona jest tak samo jak nazwa zmiennej. Musi się rozpoczynać od litery lub znaku podkreślenia. Kolejnymi znakami mogą być litery, cyfry i znaki podkreślenia. Litery duże i małe są rozróżniane.
Lista parametrów określa dane, które zostaną przekazane do funkcji. Każdy parametr jest zmienną, którą definiujemy na tej liście jako ciąg: typ nazwa, typ nazwa, ...
Klamerki określają zakres funkcji. Wszystko, co się w nich znajduje, należy do zakresu funkcji.
Treść to polecenia wykonywane przez funkcję.
Jeśli funkcja ma zwrócić wynik, to w zakresie funkcji należy umieścić rozkaz return wynik, gdzie wynik jest dowolnym wyrażeniem, które komputer wylicza i którego wynik staje się wartością zwracaną przez funkcję. Wykonanie rozkazu return przerywa funkcję i następuje powrót do miejsca wywołania. Jeśli za tym rozkazem umieścimy w funkcji jakieś instrukcje, to nie zostaną one już wykonane.
Funkcję wywołujemy poprzez jej nazwę, umieszczając w nawiasach wartości dla kolejnych parametrów.
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; // Funkcja zwraca NWD dwóch liczb a i b //------------------------------------- unsigned nwd(unsigned a, unsigned b) { unsigned r; while(b) { r = a % b; a = b; b = r; } return a; } int main() { cout << nwd(36,24) << endl << nwd(36,26) << endl; return 0; } |
Przekazywanie przez wartość oznacza, że funkcja otrzymuje w swoim parametrze wartość wyrażenia, które zostanie umieszczone w wywołaniu funkcji. Przykładem jest poprzednio napisana funkcja nwd. W parametrach a i b otrzymuje ona wartości dwóch liczb, dla których wyznacza największy wspólny dzielnik. Przeanalizujmy poniższy program:
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; void cosik(int a) { cout << "Funkcja: " << a << endl; } int main() { int x = 15; cout << "Program: " << x << endl; cosik(x); cosik(x+15); cosik(x+255); cout << "Program: " << x << endl; return 0; } |
W funkcji głównej tworzymy zmienną x i nadajemy jej wartość 15. Program wyświetla zawartość x, po czym wywołuje funkcję cosik, przekazując jej w parametrze a wartość x. Funkcja wyświetla to, co otrzymała w swoim parametrze i kończy działanie. Następuje drugie wywołanie cosik, ale teraz w parametrze a otrzyma ona zawartość x powiększoną o 15, czyli 30. W kolejnym wywołaniu cosik otrzyma w swoim parametrze wartość x powiększoną o 255, czyli 270. Na koniec program wyświetla jeszcze raz zawartość zmiennej x. Na wyjściu otrzymamy zatem:
Program: 15
Funkcja: 15
Funkcja: 30
Funkcja: 270
Program: 15
Zmieńmy nieco program:
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; void cosik(int a) { a *= a; cout << "Funkcja: " << a << endl; } int main() { int x = 15; cout << "Program: " << x << endl; cosik(x); cout << "Program: " << x << endl; return 0; } |
Teraz funkcja cosik wyznacza kwadrat otrzymanego parametru i wyświetla wartość tego kwadratu. W funkcji głównej tworzymy zmienną x i nadajemy jej wartość 15. Następnie wyświetlamy wartość x, wywołujemy funkcję cosik z wartością x jako parametrem i na koniec ponownie wyświetlamy zawartość x. Jako wynik działania otrzymamy:
Program: 15
Funkcja: 225
Program: 15
Parametr zmienił się w funkcji cosik na kwadrat, lecz nie wpłynęło to na zmianę zawartości zmiennej x, która dalej pozostała równa 15 po powrocie z funkcji cosik. Wynika z tego, że przy przekazywaniu parametru przez wartość funkcja może zrobić ze swoim parametrem cokolwiek, ale będzie to miało znaczenie tylko wewnątrz tej funkcji.
Dane do funkcji możemy również przekazywać przez referencję. W tym przypadku funkcja nie otrzymuje wartości danej, lecz samą daną, np. zmienną. Jeśli ją zmieni, to zmiana ta pozostanie w zmiennej również po zakończeniu działania funkcji.
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; void cosik(int & a) // Parametr przekazywany przez referencję { a *= a; cout << "Funkcja: " << a << endl; } int main() { int x = 15; cout << "Program: " << x << endl; cosik(x); cout << "Program: " << x << endl; return 0; } |
W programie dodaliśmy tylko jeden znak '&' przed nazwę parametru funkcji cosik. Znak ten jest w C++ operatorem referencji. Referencja oznacza, że parametr a nie jest wartością jakiegoś wyrażenia, lecz obiektem, który został udostępniony funkcji jako parametr. Funkcja cosik wyznacza kwadrat tego, co zawiera przekazany obiekt, czyli x. Obliczony kwadrat jest wstawiany z powrotem do otrzymanego obiektu, który wewnątrz funkcji cosik nosi nazwę a. Dlatego po powrocie z wywołania zmienna x zawiera kwadrat swojej początkowej wartości.
Program: 15
Funkcja: 225
Program: 255
Referencja jest przydatna przy wielu okazjach. Na przykład, jest ona jednym ze sposobów zwracania przez funkcję więcej niż jednej wartości.
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; void cosik(int a, int b, int & wd, int & rd) { wd = a / b; rd = a % b; } int main() { int i,w,r; for(i = 2; i <= 9; i++) { cosik(20 - i, i, w, r); cout << 20 - i << " : " << i << " = " << w << " i reszta " << r << endl; } return 0; } |
W powyższym przykładzie funkcja cosik przyjmuje cztery parametry. Dwa pierwsze są przekazywane przez wartość, a dwa kolejne przez referencję. Wynik jest następujący:
18 : 2 = 9 i reszta 0
17 : 3 = 5 i reszta 2
16 : 4 = 4 i reszta 0
15 : 5 = 3 i reszta 0
14 : 6 = 2 i reszta 2
13 : 7 = 1 i reszta 6
12 : 8 = 1 i reszta 4
11 : 9 = 1 i reszta 2
Zmienne tworzone wewnątrz zakresu funkcji są zmiennymi lokalnymi. Ich przestrzeń życiowa ogranicza się do wnętrza funkcji, w której zostały utworzone. Przeanalizujmy poniższy program:
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; void cosik() { int x = 555; // Zmienna lokalna dla cosik cout << "Funkcja: " << x << endl; } int main() { int x = 111; // Zmienna lokalna dla main cout << "Program: " << x << endl; cosik(); cout << "Program: " << x << endl; return 0; } |
Funkcja cosik (tym razem bez parametrów) tworzy zmienną x i nadaje jej wartość 555, co potwierdza wypisaniem odpowiedniego tekstu w oknie konsoli. Funkcja main również tworzy sobie zmienną o nazwie x i nadaje jej wartość 111, po czym wypisuje ją w oknie konsoli, wywołuje funkcję cosik i jeszcze raz wypisuje zawartość swojej zmiennej x. Otrzymujemy:
Program: 111
Funkcja: 555
Program: 111
Widzimy więc jasno, że wewnątrz funkcji x ma inną wartość niż poza nią. Staje się to oczywiste, gdy przyjmiemy do wiadomości, że są to dwie różne zmienne, które mają tę samą nazwę. Jednak nie ma tutaj niejednoznaczności, ponieważ ich przestrzenie życiowe (czyli zakresy) są różne. Jedna zmienna x żyje wewnątrz funkcji cosik, a ta druga wewnątrz funkcji main. Pierwszej nadano wartość 555, a drugiej 111. Gdy wywołamy funkcję cosik, to będzie ona korzystała ze swojej zmiennej x, czyli tej o wartości 555. Natomiast funkcja main posiada swoją zmienną x o wartości 111. W obu przypadkach są to zmienne lokalne dla cosik i main.
Inna sytuacja wystąpi w poniższym programie:
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; int x; // Zmienna globalna void cosik() { x = 555; cout << "Funkcja: " << x << endl; } int main() { x = 111; cout << "Program: " << x << endl; cosik(); cout << "Program: " << x << endl; return 0; } |
Teraz nie tworzymy zmiennych wewnątrz funkcji, lecz na zewnątrz. Każda z funkcji zmienia zawartość zmiennej x. W funkcji main zmienna x otrzymuje wartość 111, po czym w funkcji cosik zmienna ta otrzymuje nową wartość 555 i po powrocie z cosik x wciąż tę wartość przechowuje. Dlatego otrzymujemy wynik:
Program: 111
Funkcja: 555
Program: 555
W ten sposób zadeklarowana zmienna nazywa się zmienną globalną. Jest ona widoczna we wszystkich funkcjach poniżej swojej definicji – w naszym przypadku będą to obie funkcje cosik i main. Wspólna zmienna może być zarówno wygodna (np. pozwala zmniejszyć liczbę parametrów przekazywanych do funkcji) jak i niebezpieczna (przy dużej liczbie funkcji problemem może okazać się ich współpraca przy zmianach współdzielonej zmiennej globalnej). Fachowi programiści zalecają, aby nie używać zmiennych globalnych bez wyraźnej potrzeby. Na pewno mają rację.
Co się jednak stanie, jeśli w funkcji utworzymy zmienną lokalną o takiej samej nazwie jak zmienna globalna? Sprawdźmy:
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; int x; // Zmienna globalna void cosik() { int x = 555; // Zmienna lokalna cout << "Funkcja: " << x << endl; } int main() { x = 111; cout << "Program: " << x << endl; cosik(); cout << "Program: " << x << endl; return 0; } |
Jako wynik otrzymamy:
Program: 111
Funkcja: 555
Program: 111
Wygląda na to, że funkcja cosik straciła dostęp do zmiennej globalnej x na rzecz zmiennej lokalnej x. Jest to zamierzone. Otóż zmienne lokalne zawsze przysłaniają zmienne globalne o tych samych nazwach. Jeśli w cosik stworzyliśmy zmienną lokalną x, do funkcja ta będzie się odwoływać do tej lokalnej zmiennej, a nie do x globalnego, które zostało przysłonięte przez x lokalne. Na pierwszy rzut oka wygląda to zagmatwanie, lecz jest zupełnie proste w momencie, gdy zrozumiemy i zapamiętamy tę regułę. Powstaje jedynie pytanie, jak odwołać się w takim przypadku do zmiennej globalnej? Musisz skorzystać z operatora zakresu ::
// Funkcje // (C)2014 I LO w Tarnowie //------------------------ #include <iostream> using namespace std; int x; // Zmienna globalna void cosik() { int x = 555; // Zmienna lokalna cout << "Funkcja: " << x << endl; ::x = 333; // Zmiana zmiennej globalnej } int main() { x = 111; cout << "Program: " << x << endl; cosik(); cout << "Program: " << x << endl; return 0; } |
Teraz funkcja cosik zmienia również zawartość zmiennej globalnej x na 333, co uwidacznia się po powrocie do main.
Program: 111
Funkcja: 555
Program: 333
Jeśli zmienna jest tworzona na zewnątrz funkcji, to jej widoczność sięga od miejsca utworzenia do końca programu. Jest to zmienna globalna.
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe