12. díl - Funkce v jazyce C

C++ Základní konstrukce C Funkce v jazyce C

Dnešní díl tutoriálů o programovacím jazyce C je věnován velmi důležitému tématu, kterým je využívání funkcí. My jsme již seznámení s tím, že kód programu píšeme do funkce main(). To pro naše učební programy, které uměly vykonávat jen jednu jednoduchou věc, prozatím stačilo. Představte si ovšem, že píšete program, který je dlouhý několik set tisíc řádků. Určitě uznáte, že v takové nudli kódu v jednom souboru a v jedné funkci by se orientovalo velmi špatně. Navíc, pokud bychom chtěli provést nějakou stejnou posloupnost příkazů na více místech, museli bychom ji buď stále opisovat nebo v kódu skákat z místa na místo. Obě dvě možnosti jsou opět velmi nepřehledné.

Funkcionální dekompozice

O rozdělení aplikace do funkcí se někdy hovoří jako o tzv. funkcionální dekompozici. Nelekejte se termínu, jednoduše si rozmyslíme, co má naše aplikace umět a pro různé uživatelské funkce obvykle vytvoříme jednotlivé funkce ve zdrojovém kódu. V praxi se nám bude často stávat, že si budeme tvořit kromě těchto funkcí ještě nějaké pomocné, např. můžeme mít funkci pro výpis menu aplikace nebo rozdělíme nějaký složitý výpočet do více menších funkcí kvůli přehlednosti.

Funkcím se někdy říká podprogramy nebo subrutiny. Pokud funkce nevrací žádnou hodnotu (viz dále), může se ji v některých jazycích říkat procedura. U větších aplikací, které mají mnoho funkcí, se funkce sdružují do tzv. modulů. Ty vy dobře znáte např. v podobě #include <stdio.h>, kterým načítáme knihovnu (modul) pro práci se standardním vstupem a výstupem (tedy pro nás s konzolí). Podobně jsou matematické funkce soustředěné v systémovém modulu math.h. Tyto moduly nebo-li knihovny se také naučíme vytvářet.

Tvorba funkcí

Funkce je logický blok kódu, který jednou napíšeme a poté ho můžeme libovolně volat bez toho, abychom ho psali znovu a opakovali se. Funkci deklarujeme v globálním prostoru, někde nad funkcí main(). Bude vypadat podobně. Přidejme do našeho zdrojového kódu funkci, která do konzole vypíše "Ahoj, vřele tě tu vítám!". Pro názornost si poprvé uveďme kompletní zdrojový kód programu:

#include <stdio.h>
#include <stdlib.h>

void pozdrav(void)
{
    printf("Ahoj, vřele tě tu vítám!\n");
}

int main(int argc, char** argv)
{
    return (EXIT_SUCCESS);
}

První slovo void v definici funkce udává, že funkce nevrací žádnou hodnotu. Druhé void má podobný význam, určuje, že funkce nemá žádné vstupní parametry. Funkci nyní musíme zavolat, aby se spustila. Musíme to samozřejmě udělat až potom, co ji deklarujeme, jinak by ji kompilátor neznal (proto jsme ji psali nad funkci main()). Do main() napíšeme tento řádek:

pozdrav(); // zavolání funkce

Výsledek:

Funkce v programovacím jazyce C

Funkce s parametry

Funkce může mít také libovolný počet vstupních parametrů (někdy se jim říká argumenty), které píšeme do závorky v její definici. Parametry ovlivňujeme chování funkce. Mějte situaci, kdy chceme pozdravit našeho uživatele podle jména. Rozšíříme tedy stávající funkci o parametr jmeno a ten potom přidáme s konkrétní hodnotou do volání funkce:

void pozdrav(char jmeno[])
{
    printf("Ahoj, vřele tě tu vítám %s!\n", jmeno);
}

Funkci v main() následně zavoláme takto:

pozdrav("Karle"); // zavolání funkce

Kdybychom nyní chtěli pozdravit několik lidí, nemusíme otrocky psát znovu a znovu printf("Ahoj, vřele...., stačí nám pouze vícekrát zavolat naší funkci:

pozdrav("Karle");
pozdrav("Davide");
pozdrav("Mařenko");

Výsledek:

Volání funkce s parametry v jazyce C

Návratová hodnota funkce

Funkce může dále vracet nějakou hodnotu. Opusťme náš příklad s pozdravem a vytvořme tentokrát funkci, která nám spočítá obsah obdélníku. Tento obsah ovšem nebudeme chtít pouze vypsat, ale budeme ho chtít použít v dalších výpočtech. Proto výsledek funkce nevypíšeme, ale vrátíme jako návratovou hodnotu. Funkce může vracet právě jednu hodnotu pomocí příkazu return. Datový typ této hodnoty musíme uvést před definici funkce. Přidejte si do programu následující funkci:

int obsah_obdelniku(int sirka, int vyska)
{
        int vysledek = sirka * vyska;
    return vysledek;
}

V praxi by naše funkce samozřejmě počítala něco složitějšího, aby se nám ji vyplatilo vůbec programovat. V příkladu ale obdélník poslouží dobře. Funkce pojmenováváme malými písmeny, celými slovy a místo mezer používáme podtržítka. Ačkoli céčko samotné je plné zkrácenin, vy se jim vyhněte. Je totiž mnohem čitelnější funkce datum_narozeni() než datnar(), u které nemusí být na první pohled zřejmé co že to vůbec dělá.

Pokud bychom nyní chtěli vypsat obsah nějakého obdelníku, jednoduše vložíme volání funkce do funkce printf(). Jako první se spočítá obsah obdélníku, funkce tuto hodnotu vrátí a hodnota přijde jako vstupní parametr funkci printf(), která ji vypíše. Jako šířku a výšku zadejme např. 10 a 20 cm:

printf("Obsah obdélníku je: %i cm^2", obsah_obdelniku(10, 20));
návratová hodnota funkce v jazyce C

Pokud vám to přijde zmatené, vždy můžete použít ještě pomocnou proměnnou:

int obsah = obsah_obdelniku(10, 20);
printf("Obsah obdélníku je: %i cm^2", obsah);

Návratovou hodnotu funkce jsme ovšem nepoužili kvůli tomu, abychom ji jen vypisovali. Využijme nyní toho, že je výpis na nás, a vypišme součet obsahů dvou obdélníků:

int celkovy_obsah = obsah_obdelniku(10, 20) + obsah_obdelniku(20, 40);
printf("Součet obsahů obdélníku je: %i cm^2", celkovy_obsah);

Výsledek:

Využití návratové hodnoty funkce v jazyce C

Vzpomeňme si na minulé příklady, které jsme během našeho seriálu vytvořili. Můžete si je zkusit přepsat tak, abyste volali funkci. V rámci návrhu by všechen kód měl být rozdělen do funkcí (a ideálně do modulů, viz. další díly) a to zejména kvůli přehlednosti. My jsme to ze začátku kvůli jednoduchost zanedbali, nyní to prosím berte na vědomí :)

Výhoda funkcí je tedy v přehlednosti a úspornosti (můžeme napsat nějakou věc jednou a volat ji třeba stokrát na různých místech programu). Když se rozhodneme funkci změnit, provedeme změnu jen na jednom místě a tato změna se projeví všude, což značně snižuje riziko chyb. V příkladě, kde zdravíme Karla, Davida a Mařenku nám stačí změnit text pozdravu ve funkci a změní se ve všech třech voláních. Nemít kód ve funkci, museli bychom přepisovat 3 věty a v nějaké bychom mohli udělat chybu.

Rekurze

Rekurzivní funkce je taková funkce, která v těle volá samu sebe. Taková funkce potřebuje nějakou informaci, podle které pozná, kdy má skončit (tzv. ukončení rekurze). Ve funkcionálních jazycích se rekurze používá namísto cyklů. Vezměme si například cyklus for, který sčítá do desíti. Stejného výsledku můžeme docílit i rekurzí.

int cyklus(int aktualni_index, int konecny_index,int suma)
{
     if(aktualni_index == konecny_index)
          return suma;
     cyklus(aktualni_index+1,konecny_index,suma+aktualni_index);
}
cyklus(0,10,0); //začátek rekurze

//ekvivalentní zápis s for
int suma = 0;
int a;
for(a=0;a<10;a++)
     suma += a;

Jak můžete vidět, čtení rekurze není tak snadné, jak je tomu u cyklu for. Aby toho nebylo málo, použití rekurze sebou nese dodatečnou zátěž, protože se musí opakovaně předávat parametry (více v článku o kompilaci). Obecně lze velkou část programů, které používají rekurzi, přepsat do podoby bez rekurze. Pro příklad si napíšeme program, který počítá faktoriál. Předvedeme si verzi s rekurzí a verzi bez rekurze.

int factorial(int pocitane_cislo,int mezivysledek)
{
     if(pocitane_cislo != 0)
          factorial(pocitane_cislo - 1, mezivysledek * pocitane_cislo);
     else
          return mezivysledek;
}
factorial(10,1);

//pomocí cyklu for
int faktorial = 1;
int i;
for(i = 0; i < pocitane_cislo; i++)
     faktorial *= i;

S rekurzí se můžete často setkat v již existujících programech nebo na pohovorech do práce. Nicméně doporučuji rekurzi z důvodů výkonnostních spíše nepoužívat. Rekurze dokáže velice rychle zaplnit zásobník a ukončit celý program.

V příštím díle se podíváme na další se základních aspektů C - na struktury.


 

  Aktivity (4)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (5 hlasů) :
55555


 


Miniatura
Všechny články v sekci
Programování v jazyce C - Základy
Miniatura
Následující článek
Cvičení k 12. lekci Cečka

 

 

Komentáře

Avatar
ra3sk
Člen
Avatar
ra3sk:

Zdravím, chcem sa spýtať či je pomocou funkcie scanf to meno aj načítať. Predom ďakujem za odpoveď.

 
Odpovědět 25.10.2015 14:05
Avatar
Tomáš123
Člen
Avatar
Odpovídá na ra3sk
Tomáš123:

Áno ide, pozri si predchádzajúci článok ako sa načítavajú reťazce...

Odpovědět 5.11.2015 21:49
Keby nebolo Internetu Exploreru, nebolo by dnešného internetu.
Avatar
Odpovídá na ra3sk
Libor Šimo (libcosenior):

Skusal si to?
Tutorialy tu nie su len na to, aby si si ich precital, ale hlavne na to, aby si si to vyskusal sam. ;-)

Odpovědět 6.11.2015 7:35
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
ra3sk
Člen
Avatar
Odpovídá na Libor Šimo (libcosenior)
ra3sk:

Skúšal ale nie takto vo funkcí... ale určite vyskúšam.

 
Odpovědět 6.11.2015 14:30
Avatar
ra3sk
Člen
Avatar
Odpovídá na Libor Šimo (libcosenior)
ra3sk:

A ešte by som sa chcel spýtať prečo %i

 
Odpovědět 12.11.2015 13:00
Avatar
Odpovídá na ra3sk
Libor Šimo (libcosenior):

Na výpis int sa môže použiť %i alebo %d.

Odpovědět 12.11.2015 13:20
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
studnicka.ji
Člen
Avatar
studnicka.ji:
int cyklus(int aktualni_index, int konecny_index,int suma)
{
     if(aktualni_index == konecny_index)
          return suma;
     cyklus(aktualni_index+1,konecny_index,suma+aktualni_index);
}
cyklus(0,10,0); //začátek rekurze

tento kod mi hází vysledek 45... to je divné

 
Odpovědět Včera 15:53
Avatar
Libor Šimo (libcosenior):

1+2+3+4+5+6+7+8+9

Odpovědět  +1 Včera 16:31
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 8 zpráv z 8.