This is an old revision of the document!
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:
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:
int sumuj(int a, int b) { return a + b; }
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:
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 funkcji (zwana inaczej prototypem funkcji) określa typ wartości zwracanych oraz liczbę i typy argumentów. Jej ogólna postać to:
typ_wartości_zwracanej nazwa_funkcji(typ_parametru_1 nazwa_parametru_1, typ_parametru_2 nazwa_parametru_2, ..., typ_parametru_n nazwa_parametru_n);
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:
int sumuj(int a, int b);
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 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:
typ_wartości_zwracanej nazwa_funkcji([lista_parametrów]) { instrukcje; return wartość_zwracana; }
([…]
oznacza, że lista parametrów jest opcjonalna – funkcja może nie korzystać z parametrów)
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):
nazwa_funkcji([lista_argumentów]);
na przykład
sumuj(3, 5); // podczas wykonywania funkcji `a` przyjmie wartosc 3, a `b` wyniesie 5
Aby wykorzystać wartość zwracaną przez funkcję sumuj()
, możemy np. przypisać jej wartość do zmiennej:
int suma; suma = sumuj(2, 3);
Jeśli funkcja nie wymaga przekazania żadnych wartości (np. rand()
), po prostu zostawiamy nawiasy puste:
int r = rand();
(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):
void func(int a, float k); // `a` i `k` to parametry
Argument to wyrażenie użyte podczas wywołania funkcji, którego wartość zostanie przypisana do danego parametru:
func(5, 0.03); // 5 i 0.03 to argumenty
#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; }
Zwróć uwagę, że main
też jest funkcją – zwraca typ całkowity (wartość oznaczającą status zakończenia programu).
(Omówienie na powyższym przykładzie programu z funkcją podnoszącą liczbę do kwadratu.)
main()
– w powyższym przypadku zdefiniowana zostaje zmienna n
.printf()
i aby ją wykonać, zaczyna obliczać wartości wyrażeń podanych jako argumenty. Trzecim argumentem jest wywołanie innej funkcji – kwadrat()
.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.)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).kwadrat(k)
zostanie podstawiona wartość zwrócona przez tę funkcję.printf()
– w podobny sposób, co funkcja kwadrat()
.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:
#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; }
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:
void powitaj() { printf("Witaj!\n"); }
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.
(poziom trudności: )
Napisz funkcję, która wyświetli napis “Dzien dobry!” otrzymaną w argumencie ilość razy.
Nagłówek funkcji może wyglądać następująco:
void powitaj(int ile_razy)
Wskazówka: Pętla ma się znajdować w funkcji wypisującej, a nie w funkcji main()
!
(poziom trudności: )
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.