7. seminář - Funkce

TEACHINGZPC1

Funkce

Na předchozích seminářích jsme se dozvěděli jak pomocí větvení programu ovlivnit chování programu na základě vstupu, nebo pomocí cyklů opakovat n-krát stejný příkaz bez toho bychom jej museli psát n-krát pod sebou. Nabízí se tedy otázka. Je možné opakovat určitou část kódu na různých místech v programu? Ano a to pomocí funkcí. Každý program v C obsahuje alespoň jednu fuknci a to konkrétně funkci main.

Výhody funkcí

  1. Vyvarujeme se opakování kódu a tím způsobené chyby (copy-paste).
  2. Redukujeme čas potřebný pro přeložení (kompilaci) programu.
  3. Zjednodušují znovupoužítí kódu.

Definice funkce

Definice nové funkce se provádí mimo funkci main. Skládá se z návratového typu tedy jakého typu je hodnota kterou funkce vrací. Pokud nechceme, aby funkce vracela hodnotu tak návratový typ uvedeme void. Hodnotu vracíme pomocí return. Pokud má funkce návratový typ jiný než void, tak funkce musí vracet hodnotu. Dále funkce má jméno pomocí kterého funkci voláme a jako poslední následuje seznam parametrů funkce, kde každý parametr má svoje jméno a datový typ.


    type function_name(type1 p1, type2 p2, ..., typen pn) {

        // Function body

        return value;
    }

Volání funkce

Mějme funkci expt která vypočítá n-tou mocninu daněho základu. Pokud cheme tuto funkci zavolat, tak použijeme její název a do závorek dáme argumenty. V kódu níže voláme fuknci expt následovně expt(base, exponent), kde base a exponent jsou argumenty.


    #include <stdio.h>

    double expt(double base, int exponent) {
        double result = 1.0;

        for (int i = 0; i < exponent; i += 1) {
            result *= base;
        }

        return result;
    }

    int main() {
        double base;
        int exponent;

        printf("Zadejte základ a expoenent: \n");
        scanf("%lf %i", &base, &exponent);

        printf("%.1lf na %i je rovno %.1lf", base, exponent, expt(base, exponent));
        return 0;
    }

Rozsah platnosti proměnných

Platnost proměnné určuje kus kódu, kde tato proměnná existuje. Můžeme tedy přístupovat k její hodnotě případně přiradit jinou hodnotu apod. Platnost proměnné je v bloku kódu (kód mezi závorkami { }), kde byla definovaná. Hodnota proměnné se nejdříve hledá v bloku, kde ji chceme použít. Pokud není nalezena definice, tak hledáme o úroveň výše, tedy v bloku který obsahuje blok ve kterém cheme použít naši proměnnou a tak pokračujeme dokud nenajdeme definici proměnné a nebo neúspěšně hledáme v nejvyšším bloku, což je celý soubor a proměnná je definovaní mímo všechny funkce. Proměnná definovaná v bloku je nazýváná lokální proměnná a proměnnou definovanou v nejvyšším bloku navýváme globální.


    #include <stdio.h>

    //Globalni promenna
    int square_size = 10;

    // Funkce modifikuje globalni promennou
    void resize_square() {
        square_size = 20;
    }

    // Funkce vytiskne na obrazovku ctverec * a
    // pouziva hodnotu globalni promenne square_size.
    void print_square() {
        for (int row = 0; row < square_size; row += 1) {
            for (int col = 0; col < square_size; col += 1) {
                printf("*");
            }
            printf("\n");
        }
    }

    int area() {
        // Lokalni promenna ktera zastini hodnotu globalni.
        int square_size = 5
        return square_size * square_size;
    }

    int main() {
        print_square();  // Vytiskne ctverec o delce strany 10
        printf("%i", area()); // Vytiskneme obsah ctverce
        resize_square(); // Zmenime hodnotu globalni promenne

        print_square(); // Vytiskne ctverec o delce strany 20
        printf("%i", area()); // Vytiskneme obsah ctverce

        // Jake hodnoty vrati prvni a druhe volani funkce area.
        return 0;
    }

V programech bychom měli používat globální proměnné co nejměně. Může pak dojít k špagety kódu, což je situace, kdy jsou jednotlivé části programu svázány globalními proměnnými. Takový kód je nesrozumitelný pro ostatní programátory ale časem i pro jeho autora. Funkce by měli být na sobě co nejméně závislé.

Předávání parametrů hodnotou

Máme funkci swap která prohodí hodnoty v proměnných. Jaký bude výsledek?


    void swap(int a, int b) {
        int swap_help = a;
        a = b;
        b = swap_help;
    }

    int main() {
        int a = 10;
        int b = 20;

        swap(a, b);
        printf("a = %i, b = %i", a, b);
        return 0;
    }

Hodnoty se po zavolání funkce nezmění a to proto, že argumenty se předávají hodnotou. Hodnota proměnných se překopíruje na jiné místo v paměti a proměnné a a b používané v těle funkce jsou lokální proměnné funkce swap. Jednu vyjímku ale máme a to pokud předáme pole jako argument. V tomto případě jsme schopni v těle funkce měnit hodnoty argumentu.


    void erase_array(int array[], int size) {
        for(int i = 0; i < size; i += 1) {
            array[i] = 0;
        }
    }

    int main() {
        int foo[5] = {1,2,3,4,5};
        erase_array(foo, 5);

        // Zde jsou vsechny prvky pole rovny 0.

        return 0;
    }

Úkoly

Přepište úlohy z minulých kapitol za pomoci funkcí. To znamená: naprogramujte funkci, která dělá požadovanou věc, a poté ji zavolejte z funkce main. Musíte vhodně zvolit argumenty a návratovou hodnotu funkcí.

Napište program, který nalezne všechna čtyřciferná čísla, která jsou dělitelná číslem, které dostaneme jako sumu čísla tvořeného první a druhou číslicí a čísla tvořeného třetí a čtvrtou číslicí. Např. číslo 3417 je dělitelné číslem 34 + 17. Vhodné části algoritmu realizujte pomocí funkcí.

Napište program, který pro dané přirozené n spočítá sumu:

1! + (1 + 2)! + (1 + 2 + 3)! + ... + (1 + 2 + 3 + ... + n)!

(Testujte pouze pro malá n, výsledek může být obrovské číslo a může dojít k přetečení).