This shows you the differences between two versions of the page.
dydaktyka:cprog:2016:functions [2016/10/29 09:54] pkleczek utworzono |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Funkcje ====== | ||
- | |||
- | ===== Programowanie strukturalne ===== | ||
- | |||
- | W miarę pisania programów wykonujących coraz bardziej złożone operacje umieszczanie całej jego logiki w jednym bloku -- w funkcji //main()// staje się mało wygodne. Aby "rozbić" taki program na mniejsze, funkcjonalne elementy, w przypadku programowania strukturalnego stosujemy **funkcje**. Każda funkcja określa pewien z góry określony ciąg operacji (kroków algorytmu). | ||
- | |||
- | Funkcje umożliwiają między innymi: | ||
- | * dekompozycję kodu (na mniejsze fragmenty) | ||
- | * unikanie powtórzeń | ||
- | * separację i enkapsulację (ukrycie "na zewnątrz" przebiegu pewnych procesów) | ||
- | * integrację różnych fragmentów kodu dostarczanych przez różnych programistów | ||
- | |||
- | ===== Tworzenie funkcji ===== | ||
- | |||
- | W ogólnym ujęciu funkcja to wydzielony fragment kodu, spełniający określone zadanie (np. obliczenie wartości, wykonanie czynności – jak wyświetlenie czegoś na ekranie itp.). | ||
- | Zazwyczaj do poprawnego wykonania wspomnianych operacji należy dostarczyć pewnych danych wejściowych. Funkcja może również zwracać pewną wartość (np. wynik obliczeń). | ||
- | |||
- | Przykładowa definicja funkcji zwracającej sumę dwóch liczb całkowitych przekazanych do funkcji: | ||
- | <code c> | ||
- | int sumuj(int a, int b) { | ||
- | return a + b; | ||
- | } | ||
- | </code> | ||
- | |||
- | Zwróć uwagę, że język C opiera się na ścisłej typizacji. Oznacza to, że jako programiści musimy określać typ danych używanych w programie -- koniecznym jest podanie typu parametrów i wartości zwracanej przez funkcję. Pierwszy ''int'' oznacza, że funkcja zwraca wartość typu całkowitego, potem następuje nazwa funkcji (''sumuj''), a na końcu -- w nawiasach -- podana została lista parametrów funkcji (wraz z typami). | ||
- | |||
- | Funkcji używa się w trzech kontekstach: | ||
- | * **deklaracja** -- określenie nazwy funkcji, typów parametrów i typów wartości zwracanej | ||
- | * **definicja** -- określenie treści funkcji, czyli tzw. //ciała funkcji// (ang. //function body//) | ||
- | * **wywołanie** -- użycie funkcji z konkretnymi wartościami parametrów (czyli z argumentami) i ewentualne użycie wartości zwracanej | ||
- | |||
- | :!: W ANSI C funkcja musi być zadeklarowana przed pierwszym wywołaniem, ale nie musi być zdefiniowana. Innymi słowy, w kodzie programu deklaracja funkcji musi wystąpić przed pierwszym użyciem, natomiast definicję można umieścić na końcu pliku (po funkcji ''main()''). | ||
- | |||
- | ==== Deklaracja (prototyp) ==== | ||
- | |||
- | Deklaracja funkcji (zwana inaczej //prototypem funkcji//) określa typ wartości zwracanych oraz liczbę i typy argumentów. Jej ogólna postać to: | ||
- | <code> | ||
- | typ_wartości_zwracanej nazwa_funkcji(typ_parametru_1 nazwa_parametru_1, typ_parametru_2 nazwa_parametru_2, ..., typ_parametru_n nazwa_parametru_n); | ||
- | </code> | ||
- | Oczywiście funkcja może nie przyjmować argumentów i wtedy nawiasy są puste. | ||
- | |||
- | Deklaracja przytoczonej wyżej funkcji ''sumuj()'' wyglądałaby następująco: | ||
- | <code c> | ||
- | int sumuj(int a, int b); | ||
- | </code> | ||
- | Nie podajemy zatem w tym miejscu "logiki" funkcji -- zamiast tego umieszczamy średnik. | ||
- | |||
- | Dzięki zadeklarowaniu funkcji kompilator może sprawdzić, czy wywołanie funkcji pasuje do jej prototypu (i ostrzec nas o potencjalnych problemach z niezgodnością typów). | ||
- | |||
- | ==== Definicja ==== | ||
- | |||
- | Definicja funkcji to po prostu prototyp funkcji rozszerzony o jej logikę -- zamiast średnika na końcu umieszczamy blok kodu, a w nim instrukcje do wykonania w ramach funkcji. | ||
- | |||
- | Do zwracania wartości przez funkcję służy instrukcja ''return'' -- powoduje ona przerwanie dalszego wykonania funkcji i zwrócenie wartość wyrażenia stojącego na prawo od tego słowa kluczowego. | ||
- | |||
- | Ogólna postać definicji funkcji: | ||
- | <code> | ||
- | typ_wartości_zwracanej nazwa_funkcji([lista_parametrów]) | ||
- | { | ||
- | instrukcje; | ||
- | return wartość_zwracana; | ||
- | } | ||
- | </code> | ||
- | (''[...]'' oznacza, że lista parametrów jest opcjonalna -- funkcja może nie korzystać z parametrów) | ||
- | ==== Wywołanie funkcji ==== | ||
- | |||
- | Aby wywołać funkcję (czyli wykonać instrukcje w ramach funkcji), należy w kodzie programu umieścić instrukcję składającą się z nazwy funkcji oraz nawiasów okrągłych, wewnątrz których podaje się wyrażenia oddzielone przecinkami -- argumenty funkcji (czyli konkretne wartości parametrów, już bez podawania typów danych): | ||
- | <code> | ||
- | nazwa_funkcji([lista_argumentów]); | ||
- | </code> | ||
- | na przykład | ||
- | <code c> | ||
- | sumuj(3, 5); // podczas wykonywania funkcji `a` przyjmie wartosc 3, a `b` wyniesie 5 | ||
- | </code> | ||
- | |||
- | Aby wykorzystać wartość zwracaną przez funkcję ''sumuj()'', możemy np. przypisać jej wartość do zmiennej: | ||
- | <code c> | ||
- | int suma; | ||
- | suma = sumuj(2, 3); | ||
- | </code> | ||
- | |||
- | Jeśli funkcja nie wymaga przekazania żadnych wartości (np. ''rand()''), po prostu zostawiamy nawiasy puste: | ||
- | <code c> | ||
- | int r = rand(); | ||
- | </code> | ||
- | |||
- | ==== Parametr czy argument? ==== | ||
- | |||
- | (Te dwa terminy łatwo pomylić, stąd poniższe podsumowanie.) | ||
- | |||
- | **Parametr** to zmienna, która pojawia się w nagłówku funkcji (np. przy okazji deklaracji bądź definicji): | ||
- | <code c> | ||
- | void func(int a, float k); // `a` i `k` to parametry | ||
- | </code> | ||
- | |||
- | **Argument** to wyrażenie użyte podczas wywołania funkcji, którego wartość zostanie przypisana do danego parametru: | ||
- | <code c> | ||
- | func(5, 0.03); // 5 i 0.03 to argumenty | ||
- | </code> | ||
- | ===== Przykład użycia funkcji ===== | ||
- | |||
- | <code c> | ||
- | #include <stdio.h> | ||
- | #include <stdlib.h> | ||
- | |||
- | // Deklaracja (inaczej "prototyp") funkcji. | ||
- | int kwadrat(int n); | ||
- | |||
- | int main() | ||
- | { | ||
- | int n = 3; | ||
- | |||
- | // Wywolanie funkcji kwadrat(), zwrocona wartosc zostaje przekazana jako | ||
- | // argument funkcji printf()... | ||
- | printf("%d^2 = %d\n", n, kwadrat(n)); | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | // Definicja funkcji. | ||
- | int kwadrat(int n) { | ||
- | // Podnies liczbe `n` do kwadratu i zwroc ta wartosc. | ||
- | return n * n; | ||
- | } | ||
- | </code> | ||
- | |||
- | Zwróć uwagę, że ''main'' też jest funkcją -- zwraca typ całkowity (wartość oznaczającą status zakończenia programu). | ||
- | |||
- | ===== Jak to wszystko działa? ===== | ||
- | |||
- | (Omówienie na powyższym przykładzie programu z funkcją podnoszącą liczbę do kwadratu.) | ||
- | |||
- | * Wykonanie __każdego__ programu konsolowego w języku C zaczyna się od pierwszej instrukcji funkcji ''main()'' -- w powyższym przypadku zdefiniowana zostaje zmienna ''n''. | ||
- | * Następnie program natrafia na wywołanie funkcji ''printf()'' i aby ją wykonać, zaczyna obliczać wartości wyrażeń podanych jako __argumenty__. Trzecim argumentem jest wywołanie innej funkcji -- ''kwadrat()''. | ||
- | * Sterowanie przechodzi więc do pierwszej instrukcji __definicji__ funkcji ''kwadrat()''. Wcześniej tworzona jest __nowa__ zmienna ''n'', która "przesłania" zmienną ''n'' utworzoną w funkcji ''main()''. Oznacza to, że choć początkowa wartość zmiennej ''n'' utworzonej w funkcji ''kwadrat()'' wynosi tyle samo, co wartość zmiennej ''n'' utworzonej w funkcji ''main()'', to jednak zmiana wartości ''n'' wewnątrz funkcji ''kwadrat()'' nie powoduje zmiany wartości zmiennej ''n'' z funkcji ''main()''. (Przykład ilustrujący tą właściwość -- poniżej.) | ||
- | * Akurat tak się składa, że pierwszą (i jedyną) instrukcją funkcji ''kwadrat()'' jest instrukcja ''return''. Wykonanie instrukcji ''return'' powoduje powrót sterowania do miejsca wywołania funkcji (a więc żadne dalsze instrukcje wewnątrz funkcji ''kwadrat()'' nie zostaną wykonane). | ||
- | * Za wyrażenie ''kwadrat(k)'' zostanie podstawiona wartość zwrócona przez tę funkcję. | ||
- | * Teraz wykonana zostanie funkcja ''printf()'' -- w podobny sposób, co funkcja ''kwadrat()''. | ||
- | * Instrukcja ''return 0;'' na końcu funkcji ''main()'' powoduje powrót sterowania do systemu operacyjnego. | ||
- | |||
- | ---- | ||
- | |||
- | Przykład, że zmienna zadeklarowana wewnątrz jednej funkcji //przesłania// zmienne z innej funkcji: | ||
- | <code c> | ||
- | #include <stdio.h> | ||
- | #include <stdlib.h> | ||
- | |||
- | void zmien(int n) | ||
- | { | ||
- | int i = 4; | ||
- | n = 1; | ||
- | |||
- | printf("ZMIEN: i = %d, n = %d\n", i, n); | ||
- | } | ||
- | |||
- | int main() | ||
- | { | ||
- | int n = 7; | ||
- | int i = 0; | ||
- | |||
- | printf("MAIN (1): i = %d, n = %d\n", i, n); | ||
- | zmien(n); | ||
- | printf("MAIN (2): i = %d, n = %d\n", i, n); | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | ===== Typ "void" ===== | ||
- | |||
- | Czasem nie ma potrzeby, by funkcja cokolwiek zwracała -- a mimo to język C wymaga, aby określić typ zwracanej wartości. W takiej sytuacji stosuje się typ ''void'', czyli właśnie "nic". | ||
- | |||
- | Na przykład poniższa funkcja ma za zadanie jedynie wypisać na ekran powitanie: | ||
- | <code c> | ||
- | void powitaj() { | ||
- | printf("Witaj!\n"); | ||
- | } | ||
- | </code> | ||
- | |||
- | :!: Wewnątrz funkcji nie zwracającej żadnej wartości (tj. funkcji typu //void//) również można stosować instrukcję ''return;'' -- powoduje ona wtedy jedynie przerwanie dalszego wykonywania funkcji i powrót sterowania do miejsca wywołania funkcji, bez zwracania jakiejkolwiek wartości. | ||
- | ===== Zadania podsumowujące ===== | ||
- | |||
- | ==== Zadanie GREET ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>1/4}}) | ||
- | |||
- | Napisz funkcję, która wyświetli napis "Dzien dobry!" otrzymaną w argumencie ilość razy. | ||
- | |||
- | Nagłówek funkcji może wyglądać następująco: | ||
- | <code c> | ||
- | void powitaj(int ile_razy) | ||
- | </code> | ||
- | |||
- | //Wskazówka: Pętla ma się znajdować w funkcji wypisującej, a nie w funkcji ''main()''!// | ||
- | ==== Zadanie MAX ==== | ||
- | |||
- | ([size=10]poziom trudności:[/size] {{stars>1/4}}) | ||
- | |||
- | Napisz funkcję ''max()'', posiadającą dwa parametry (''a'' i ''b'') będące liczbami całkowitymi. Funkcja powinna zwrócić większą spośród dwóch przekazanych jako argumenty wartości. | ||