User Tools

Site Tools


dydaktyka:cprog:2016:functions

This is an old revision of the document!


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:

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:

  • 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:

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

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)

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):

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();

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):

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

Przykład użycia funkcji

#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).

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:

#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;
}

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:

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.

Zadania podsumowujące

Zadanie GREET

(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()!

Zadanie MAX

(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.

dydaktyka/cprog/2016/functions.1477727680.txt.gz · Last modified: 2020/03/25 11:46 (external edit)