Podstawy informatyki Funkcje Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka
Funkcja Funkcja – podprogram o swojej własnej nazwie, który może być wywołany z innej części programu dowolną liczbę razy. Pozwala na definiowanie własnych „instrukcji”. Funkcja dostaje argumenty i zwraca wartość. Argument 1 Funkcja Argument 2 Argument n Wartość
Funkcja Operator wywołania funkcji: (). Funkcje są również definiowane/deklarowane za pomocą (). Jeżeli po nazwie znajduje się otwarty nawias okrągły, to nazwa jest nazwą funkcji! Definiując/deklarując funkcję podajemy jej nazwę, liczbę i typy argumentów oraz typ wartość zwracanej, np.: double pole(double,double); POST () PRE + - ! ~ 15 14* / % > 11 >= 10== != 9& 8^ 7| 6&& 5|| 4? : 3= *= /= %= += -= >= &= |= ^= 2 1,
Deklaracja funkcji Każda funkcja musi być zadeklarowana. Deklaracja zaznajamia kompilator z nazwą funkcji – nie mówi co funkcja robi! Przykłady deklaracji: double pole(double); int liczba_Fibb(int); void wypisz(int, int, double); void funkcja(); Na końcu każdej deklaracji znajduje się średnik!
Definicja funkcji Definicja funkcji jest równoznaczna z napisaniem jej treści, np.: double pole(double r) { double P=3.14*r*r; return P; } Przed wywołaniem funkcji wymagana jest jej deklaracja (niekoniecznie definicja). Sama funkcja może być zdefiniowana później albo wręcz w innym pliku. Definicja jest jednocześnie deklaracją (ale nie na odwrót)!
Zwracanie wartości Funkcja zwraca wartość przez instrukcję: return wyrażenie; Wyrażenie musi być typu zgodnego z typem zwracanym przez funkcję lub dającego się skonwertować do typu zwracanego double pole(double r) { return 3.14*r*r; } wyrażenie typu double
Zwracanie wartości W funkcji może być kilka instrukcji return Zawsze musimy zagwarantować, żeby każda ścieżka wykonania kończyła się instrukcją return. Jeśli funkcja zwraca typ void, czyli nie zwraca niczego, to wewnątrz funkcji nie musi być instrukcji return. Jeśli pojawi się instrukcja return to nie może stać za nią żadne wyrażenie: return wyrażenie; return; BŁĄD OK
Deklaracja a definicja funkcji double pole(double); int main() { double r=5,P; P=pole(r); } double pole(double r) { double P=3.14*r*r; return P; } DEKLARACJA WYWOŁANIE DEFINICJA
Deklaracja i definicja void f(int); int main() { f(5); return 0; } void f(int a) { cout << a << endl; } void f(int a) { cout << a << endl; } int main() { f(5); return 0; }
Deklaracja funkcji double pole(double); double objetosc(double,double); int main() { double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double pole(double r) { return 3.14*r*r; } double objetosc(double r, double h) { return pole(r)*h; } double pole(double); int main() { double objetosc(double,double); double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double pole(double r) { return 3.14*r*r; } double objetosc(double r, double h) { return pole(r)*h; }
Deklaracja funkcji int main() { double pole(double); double objetosc(double,double); double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double pole(double r) { return 3.14*r*r; } double objetosc(double r, double h) { return pole(r)*h; } int main() { double pole(double); double objetosc(double,double); double r=5,h=10,P,V; P=pole(r); V=objetosc(r,h); cout<<P<<endl<<V<<endl; } double objetosc(double r, double h) { return pole(r)*h; } double pole(double r) { return 3.14*r*r; } BŁĄD
Funkcja lokalna int main() { double pole(double); double r=5,P; P=pole(r); cout<<P<<endl; double pole(double r) { return 3.14*r*r; } int main() { double pole(double r) { return 3.14*r*r; } double r=5,P; P=pole(r); cout<<P<<endl; } LOCAL FUNCTION DEFINITIONS ARE ILLEGAL
Deklaracja a definicja funkcji double pole(double); int main() { double r=5,P; P=pole(r); } double pole(double r) { double P=3.14*r*r; return P; } Wiązanie na etapie kompilacji. Wczesne wiązanie.
Przykład późnego wiązania #include using namespace std; class pojazd { protected: int l_kol, max_sp; public: pojazd(int a,int b):l_kol(a),max_sp(b){} virtual void wypisz(); }; class samochod: public pojazd { double poj_s; public: samochod(int a,int b,double c):pojazd(a,b), poj_s(c){} void wypisz(); }; int main() { pojazd P(2,30),*W; samochod S(4,180,1.9); char a; cout<<"P czy S\n"; cin>>a; if (a=='P') W=&P; else W=&S; W->wypisz(); return 0; } Wiązanie na etapie wykonywania. Późne wiązanie.
Argumenty double objetosc(double,double); int main() { double r=5,h=10,V1,V2; V1=objetosc(r,h); V2=objetosc(1.2,6.7); return 0; } double objetosc(double r,double h) { return 3.14*r*r*h; } argumenty formalne argumenty aktualne
Przesyłanie argumentów przez wartość Do funkcji przesyłana jest kopia obiektu (argumentu aktualnego) wywołania funkcji. Wartość ta służy do inicjalizacji parametru formalnego. Funkcja pracuje na kopii argumentu aktualnego. Parametr formalny jest lokalną automatyczną zmienną o zakresie ważności funkcji => po opuszczeniu funkcji kopia znika, wobec tego znika też to, co zostało zrobione w funkcji na tej kopii.
Przesyłanie argumentów przez wartość void f(int); int main() { int a=10; cout << a << endl; f(a); cout << a << endl; return 0; } void f(int a) { a*=2; cout << a << endl; } a a
Przesyłanie argumentów przez referencje Argumenty można przesyłać do funkcji przez referencję. Funkcja nie działa na kopii argumentu aktualnego, ale na referencji do zmiennej będącej argumentem aktualnym. Zmiana wartości argumentu aktualnego przekazanego przez referencję jest widoczna w miejscu wywołania funkcji.
Przesyłanie argumentów przez referencje void f(int &); int main() { int a=10; cout << a << endl; f(a); cout << a << endl; return 0; } void f(int &b) { b*=2; cout << b << endl; } ab
Argumenty domniemane Wszystkie lub część argumentów funkcji może posiadać wartości domniemane (domyślne) Argumenty domniemane można wyspecyfikować tylko dla ostatnich parametrów funkcji. Jeśli podano argument domyślny dla pierwszego parametru pozostałe parametry także muszą mieć wartości domyślne. int potega(int P, int W=2); lub int potega(int P=1, int W=2); Zapis: int potega(int P=1, int W); generuje błąd.
Argumenty domniemane Wyjątkiem jest rozszerzanie deklaracji: void f(int,int,int=1); void f(int,int=5, int); void f(int=9, int, int); Każdy argument domniemany można podać tylko raz, przy deklaracji funkcji. void f(int,int=5, int=1); void f(int, int, int=2); BŁĄD
Argumenty domniemane void f(int=1,int=2,int=3); int main() { f(); f(10); f(10,20); f(10,20,30); f(10,,30); return 0; } void f(int a,int b,int c) { … } BŁĄD a=1 b=2 c=3 a=10 b=2 c=3 a=10 b=20 c=3 a=10 b=20 c=30
Argumenty domniemane void f(int=1,int=2,int=3); int main() { f((10,20,30)); f((10,20),30); f(10,(20,30)); (f(10,20,30)); return 0; } void f(int a,int b,int c) { … } a=30 b=2 c=3 a=20 b=30 c=3 a=10 b=30 c=3 a=10 b=20 c=30
Funkcja inline Kompilator napotykając funkcję zdefiniowaną jako inline umieszcza treść tej funkcji tam, gdzie jest ona wywoływana. Funkcje inline powinny być stosowane dla funkcji, których treść jest bardzo krótka i które wywoływane są w programie wiele razy. Ostateczną decyzję o tym czy funkcja zostanie rozwinięta w miejscu wywołania podejmuje kompilator. Słowo kluczowe inline jest dla niego sugestią. Jeśli funkcja jest oznaczona jako inline, to jej definicja musi być umiejscowiona przed jej pierwszym użyciem. Sama deklaracja nie wystarczy!
Zmienne lokalne statyczne Zmienne globalne – zakres ważności: obszar pliku, są wstępnie inicjalizowane zerami. Zmienne lokalne (automatyczne) – zakres ważności lokalny, nie są wstępnie inicjalizowane, zawierają „śmieci”. Zmienne lokalne statyczne – są wstępnie inicjalizowane zerami i zajmują w pamięci ten sam obszar, co zmienne globalne. int a; int main() {... } int main() { int a;... }
Zmienne lokalne statyczne Słowo kluczowe static przed zmienną wewnątrz funkcji pozwala na definiowanie zmiennej, która po zakończeniu działania funkcji nie jest „usuwana z pamięci” ale zachowuje swoje wartości do następnego wywołania funkcji. Inicjalizacja zmiennej zadeklarowanej jako static dokonywana jest tylko raz, przy pierwszym wykonaniu tej funkcji.
Zmienne lokalne statyczne #include using namespace std; void licznik(); int main() { for (int i=0; i<5; i++) licznik(); return 0; } void licznik() { int ile=0; ile++; cout << ile << endl; }
Zmienne lokalne statyczne #include using namespace std; void licznik(); int main() { for (int i=0; i<5; i++) licznik(); return 0; } void licznik() { static int ile=0; ile++; cout << ile << endl; }
Rekurencja Rekurencja (rekursja) – wywoływanie funkcji przez samą siebie Jeśli problem rzeczywiście jest rekurencyjny, to program rozwiązujący go w sposób rekursywny jest przejrzysty. int silnia(int n) { int s=n; while (n-=1) s*=n; return s; } int silnia(int n) { if (n==0) return 1; return n*silnia(n-1); }
Prezentacja udostępniona na licencji Creative Commons: Uznanie autorstwa, Na tych samych warunkach 3.0. Pewne prawa zastrzeżone na rzecz autorów. Zezwala się na dowolne wykorzystywanie treści pod warunkiem wskazania autorów jako właścicieli praw do prezentacji oraz zachowania niniejszej informacji licencyjnej tak długo, jak tylko na utwory zależne będzie udzielana taka sama licencja. Tekst licencji dostępny jest na stronie: