Funkce

8. díl - Funkce

Petr Sklenička       18. 12. 2010       C++/C       9300 zobrazení

Cílem tohoto dílu je seznámit se s funkcemi. Vysvětlíme si proto, co to funkce jsou, jak se deklarují a definují, jak se volají atd. Samozřejmě nebudou chybět krátké ukázky kódu.

Co je to funkce?

Ač to někoho z Vás možná překvapí, s funkcemi, nebo spíše s funkcí, jsme se již setkali. Byla to funkce main. Tato funkce se automaticky zavolá při spuštění každého programu, napsaném v C++. Funkce je vlastně podprogram, který dokáže nějakým způsobem zpracovat data a vrátit nějakou hodnotu. Každá funkce má svůj název a v případě, že se tento název objeví někde v průběhu provádění programu, předá se řízení do těla funkce s tímto názvem. Říkáme tomu volání funkce. Po provedení těla funkce se program vrátí zpět a pokračuje na řádku, který následuje za voláním funkce.

 

Proč se funkce používají?

Představte si, že píšete program, ve kterém často provádíte nějaký výpočet - pro jednoduchost uvažujme například výpočet faktoriálu nějakého čísla. K tomu, abychom faktoriál spočítali, můžeme použít například cyklus. Jestliže se nám v programu bude tento výpočet opakovat, bude se nám opakovat i kód cyklu. Napíšeme tedy několik řádků kódu, přičemž budou víceméně stejné. To už na první pohled není příliš praktické. Proto máme funkce. Vytvoříme si tedy funkci, která vypočítá faktoriál nějakého čísla a v programu pak vždy, když budeme potřebovat znát faktoriál, zavoláme pouze naší funkci. S použitím funkcí je i pak samotný kód přehlednější a snadněji udržovatelný.

 

Návratový typ a parametry funkce

Když voláme funkci, očekáváme, že provede nějaký úkol a vrátí nám výsledek. Pod pojmem výsledek si můžeme představit cokoliv - číslo, hodnotu true nebo false, pointer na objekt (bude vysvětleno v dalších dílech) atd. U každé funkce, kterou v programu vytvoříme, musíme říct, jaký výsledek bude vracet - lépe řečeno, jaký datový typ bude vracet. Zkusme si představit primitivní funkci, která bude sčítat dvě celá čísla typu int. Sečteme-li taková dvě čísla, výsledek bude opět celé číslo typu int - proto jako návratový typ funkce zvolíme int.

Otázkou je, jaká dvě čísla má funkce sečíst (mám na mysli konkrétní hodnoty). Tyto čísla musíme funkci nějakým způsobem sdělit. K tomu nám slouží parametry funkce. U parametrů opět musíme uvést jejich datový typ.

int Soucet(int cislo1, int cislo2);

Takto by mohla vypadat deklarace funkce Soucet. První klíčové slovo (int) udává, že funkce bude vracet hodnotu typu int. Následuje název naší funkce, tedy Soucet. To, co je v závorce, je seznam parametrů. Naše funkce má za úkol sčítat dvě čísla, stačí tedy dva parametry.

 

Deklarace a definice funkce

Při používání funkcí je nutné funkci nejdříve deklarovat, poté definovat. Deklarací oznámíme kompilátoru název, návratový typ a parametry funkce. Jednoduše řečeno tím kompilátoru řekneme "bude existovat taková a taková funkce, která vrací to a to a má parametry takové a takové". Deklarace funkce se nazývá prototyp funkce. Definicí funkce říkáme kompilátoru něco jiného - a to jak přesně funkce funguje.

 

Deklarace funkce

Deklarace funkce se dá provést třemi způsoby:

  1. Prototyp funkce napíšeme do souboru, ve kterém se funkce používá
  2. Prototyp funkce napíšeme do jiného souboru a tento soubor se pak pomocí direktivy #include zahrne do programu
  3. Funkci definujeme předtím, než ji zavoláme - v tomto případě definice funkce funguje jako svoje vlastní deklarace

Třetí bod absolutně nedoporučuji. Není totiž dobré, když se funkce v souboru musí psát v daném pořadí - v případě pozdějších změn programu se kód velmi špatně předělává. Další problém třetího bodu je ten, že máme-li nějakou funkci A, která volá funci B, přičemž funkce B volá opět funkci A, není možné vůbec třetí bod použít. Nelze totiž definovat funkci A před funkcí B a zároveň funkci B před A. Po delší době programovaní sami zjistíte, že třetí způsob opravdu není vhodný, proto vždy pište protopyp funkce - u velmi banálních programů je možné udělat výjimku.

 

Ukázka funkce, rozbor kódu

Dost bylo teorie, podívejte se tedy proto na následují kód, ve kterém používáme funkci Soucet.

#include <iostream>
using namespace std;

int Soucet(int cislo1, int cislo2);     // prototyp funkce

int main()
{
    int vysledek;
    vysledek = Soucet(3, 5);            // volani funkce a prirazeni vracene hodnoty do promenne vysledek
    cout << vysledek;
    return 0;
}

int Soucet(int cislo1, int cislo2)      // zde jiz neni strednik
{
    return cislo1 + cislo2;
}

Tento kód na obrazovku vypíše číslo 8. Nejprve se podívejte na prototyp funkce. S tím už jsme se setkali výše, všimněte si ale, že za ním následuje středník. Teď se podívejte na to, jak samotná funkce pracuje. Jde o primitivní funkci, její tělo se vešlo na jediný řádek. Klíčové slovo return říká, co má funkce vrátit. V našem případě vrací součet čísel cislo1 a cislo2. Pozor - pokud byste pod řádek, kde je return, dopsali nějaký kód, neprovedl by se! Jakmile program dojde ke slovu return, funkce končí a další případný kód je ignorován. Nyní je třeba ještě objasnit volání funkce, které se provádí ve funkci main. Vidíte, že v závorce za názvem funkce už jsou jen dvě čísla. Proměnná cislo1 tedy dostane hodnotu 3, cislo2 bude mít hodnotu 5. Zavolá se funkce, ta vrátí součet těchto čísel a ten se přiřadí do proměnné vysledek.

Zkusíme si napsat ještě jednu funkci, tentokrát pro výpočet faktoriálu. Pro ty, kteří ještě nevědí, nebo kteří již zapomněli, co to faktoriál čísla je, uvádím krátké připomenutí. Faktoriál čísla n (značíme n!) je číslo, které dostaneme součinem čísel menších nebo rovných n, pokud n je kladné. Čili 5! = 120, protože 5 * 4 * 3 * 2 * 1 = 120. Podotýkám, že faktoriál čísla 0 je 1 - opravdu tomu tak je, i když se to nezdá. Jak by tedy mohla vypadat funkce?

int Faktorial(int n)
{
    if (n == 0)
        return 1;
    
    int vysledek = n;
    for (int i = n - 1; i > 0; i--)
    {
        vysledek *= i;
    }
    return vysledek;
}

Všimněte si, že klíčové slovo return jsem zde použil dvakrát. V případě, že chceme zjistit faktoriál čísla 0, víme, že je to 1, nemusíme tedy nic počítat, vrátíme jedničku a konec. V případě, že podmínka neplatí, faktoriál čísla vypočítáme pomocí cyklu. Zde si můžete všimnout, že cyklus běží "směrem dolů", ne tedy od 0 a výše, jako tomu bylo v dílech s cykly.

Nyní si uvedeme ještě jeden příklad, ve kterém si ukážeme, že je možné mít nějakou funkci a z této funkce volat ještě další funkci. Jako pěkný příklad nám k tomu poslouží výpočet kombinačního čísla. Opět krátké připomenutí - kombinační číslo je číslo tvaru n nad k, přičemž jeho hodnota se vypočítá jako n! / [k!(n-k)!]. Vidíme, že při výpočtu se počítají i faktoriály, využijeme tedy již výše uvedenou funkci pro výpočet faktoriálu. Celý kód by pak mohl vypadat takto:

#include <iostream>
using namespace std;
int Faktorial(int n);
int KombinacniCislo(int n, int k);

int main()
{
    cout << KombinacniCislo(5, 2) << "\n";
    return 0;
}

int Faktorial(int n)
{
    if (n == 0)
        return 1;
    
    int vysledek = n;
    for (int i = n - 1; i > 0; i--)
    {
        vysledek *= i;
    }
    return vysledek;
}

int KombinacniCislo(int n, int k)
{    
    return Faktorial(n) / (Faktorial(k) * Faktorial(n-k));
}

Tento kód vypíše na obrazovku číslo 10 (pět nad dvěma je skutečně 10). Podívejte se na řádek ve funkci main, kde se vypisuje hodnota na obrazovku. Určitě vidíte, že jsme si hodnotu, kterou nám funkce KombinacniCislo vrací, nepřiřadili do žádné proměnné, ale rovnou jsme ji vypsali. Co se týče funkce KombinacniCislo, je velmi jednoduchá - jde v ní jen o matematický výpočet.

Myslím, že v tuto chvíli by Vám měli být funkce alespoň trochu jasné. Aby toho však nebylo málo, zmíníme ještě věc, která souvisí s funkcemi, a tou je rekurze.

 

Rekurze

V předchozím příkladu jsme viděli, že není problém, když nějaká funkce volá jinou funkci. Není však žádný problém, pokud funkce bude volat sama sebe. Na první pohled možná trochu nelogické, ale jde to. Tomu se říká rekurze. Existují dva typy rekurze - buď funkce volá sama sebe, nebo funkce A volá funkci B, která opět volá funkci A. V tomto díle si ukážeme jeden klasický školní příklad, kdy funkce bude volat sama sebe.

Některé problémy se právě za použití rekurze dají řešit velmi pěkně. V případě, že píšeme nějakou rekurzivní funkci, je třeba si dát pozor, abychom neudělali nějakou drobnou chybu, která by mohla vést k selhání programu. Je totiž potřeba si uvědomit, že zavolá-li funkce sebe samotnou, nově zavolaná funkce opět zavolá sebe samotnou - a tak pořád dokola. Tyto funkce tedy vyžadují jistou podmínku, při které se rekurze ukončí. Pokud jste se ještě s rekurzní nikdy nesetkali, asi Vám to bude na první pohled připadat trochu chaotické. Není to ale až tak složité, vzpomínám si, že já jsem také hned rekurzi úplně nechápal.

Schválně jsem dnešní díl spojil i s trochou matematiky (těm, kteří nemají matematiku příliš v lásce, se omlouvám) - ukázali jsme si výpočet faktoriálu pomocí cyklu. Výpočet faktoriálu však lze provést i rekurzivně. Takto vypadá kód:

int Faktorial(int n)
{
    if (n == 0)
        return 1;
    else
        return n * Faktorial(n - 1);
}

V těle funkce je nejprve podmínka, při které se rekurze ukončí. Zavoláme-li funkci a předáme-li ji číslo 0, vrátí nám číslo 1. V opačném případě se zavolá znovu funkce Faktorial, předáváme ji však číslo o jedničku menší. Zkusme si rozebrat, jak to bude v případě, že budeme zjišťovat faktoriál čísla 3 (který je 6).

Při prvním volání podmínka neplatí, voláme tedy funkci znovu, předáváme ji však číslo 2 a hodnotu, kterou vrátí, násobíme číslem 3. Při volání funkce s parametrem 2 však podmínka stále neplatí, funkce se tedy volá znovu - tentokrát s parametrem 1. Opět se provede volání funkce, tentokrát už naposledy, neboť ji předáváme číslo 0 a tím už je podmínka splněna - funkce vrací jedničku. Tato jednička je pak zpětně násobena dvojkou, následně trojkou. Vychází číslo 6 a rekurze končí.

Přiznávám, že je to trochu komplikované, ale pokud budete programovat, určitě rekurzi pochopíte. Další využití rekurze je třeba u některých operací, které se provádějí nad binárními vyhledávacími stromy, nebo například u vyhledávání půlením intervalu. O tom ale někdy příště.

Pro tento díl je to vše, doufám, že jste funkce alespoň trochu pochopili, budeme se s nimi totiž setkávat i v dalších dílech. Nakonec přeju všem čtenářům pěkné Vánoce a do Nového roku spoustu programátorských úspěchů.

 

hodnocení článku

5 bodů / 5 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

 

Mohlo by vás také zajímat

C++/CLI a interoperabilita managed a unmanaged kódu - díl 1.: Úvod do jazyka a základní konstrukce

V tomto článku je popsána nadstavba C++ pro práci s .NET prostředím zvaná C++/CLI umožňující vytvářed mixed assembly obsahující jak managed tak unmanaged kód. V prvním díle je popsána myšlenka jazyka a základní syntaktické konstrukty (základní typy, podmínky, cykly, pole, namespace a část tříd a objektů). U čtenáře je předpokládána znalost .NET frameworku a nativního programování nejlépe v C++ (alespoň syntaxi a základy).

C++/CLI a interoperabilita managed a unmanaged kódu - díl 2.: Složitější konstrukty, low-level přístup a generické programování

V tomto článku je popsána nadstavba C++ pro práci s .NET prostředím zvaná C++/CLI umožňující vytvářed mixed assembly obsahující jak managed tak unmanaged kód. V tomto díle jsou popsány pokročilejší partie jazyka týkající se hlavně objektů, low-level přístupu k managed objektům a generického programování.

Virtuální souborový systém

Článek pojednává o způsobu návrhu virtuálního souborového systému. Před čtením Vám doporučuji stáhnout si a přečíst zadání, abyste věděli o co vlastně jde. Ke stažení jsou i zdrojové kódy v jazyce C++. Aplikace asi nemá žádné velké využití, nicméně přišlo mi to jako zajímavý problém - jednalo se o semestrální projekt.

 

 

Nový příspěvek

 

Diskuse: Funkce

ono by bylo ještě dobre u te rekurze zminit že při volaní sama sebe se ta funkce uklada na zasobnik dokud neni splněna podminka a pak se z toho zasobniku postupne odstranuje...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Funkce

Zdravím

Díky za super článek a zároveň jsem rád, že, i když se na publikace jednotlivých článků na webu dost čeká,tak seriál C++ nestojí..

Jen mám jeden dotaz.Jaký mám nastavit typ funkce, když chci vracet text ?

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Chcete-li vracet text, asi nejjednodušší způsob by mohl vypadat takto:

#include <iostream>
#include <string>
using namespace std;
string MojeFunkce();

int main()
{
     // nejaky kod
     return 0;
}

string MojeFunkce()
{
     string MujText = "Toto je text, ktery funkce vraci.\n"
     return MujText;
}

Ještě jedna rada - můžete taky nevracet nic a přímo ve funkci nechat text vypsat na obrazovku (zde ale nevím, jestli je to žádoucí) - pokud ano, klidně můžete říct, že funkce nebude mít žádnou návratovou hodnotu (klíčové slovo void) a přímo ve funkci mít příkaz cout.

Dále by to šlo udělat i pomocí pointeru na char, ukazatele jsem však ještě nevysvětloval, proto raději použijte string.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Jo, díky

Chtěl jsem jen zjistit,jak to udělat s tím textem, přestě to, co jste napsal, jsem hledal ...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

nebo muzes i takto bez knohovny string

char* retezec()

{

char *neco = "Cau";

return neco;

}

int main()

{

cout<<retezec();

}

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback