IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

12. diel - Funkcie v jazyku C

V predchádzajúcom cvičení, Riešené úlohy k 10. a 11. lekcii Céčka, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 10. a 11. lekcii Céčka , sme sa zoznámili s matematickou knižnicou math.h. Dnešné tutoriál o programovacom jazyku C je venovaný veľmi dôležitej téme, ktorým je využívanie funkcií. My sme už zoznámenie s tým, že kód programu píšeme do funkcie main(). To pre naše učebné programy, ktoré vedeli vykonávať len jednu jednoduchú vec, zatiaľ stačilo. Predstavte si však, že píšete program, ktorý je dlhý niekoľko sto tisíc riadkov. Určite uznáte, že v takej rezance kóde v jednom súbore a v jednej funkcii by sa orientovalo veľmi zle. Navyše, ak by sme chceli vykonať nejakú rovnakú postupnosť príkazov na viacerých miestach, museli by sme ju buď stále opisovať alebo v kóde skákať z miesta na miesto. Obe dve možnosti sú opäť veľmi neprehľadné.

Funkcionálne dekompozícia

O rozdelení aplikácie do funkcií sa niekedy hovorí ako o tzv. Funkcionálne dekompozícii. Neľakajte sa termíne, jednoducho si rozmyslíme, čo má naša aplikácia vedieť a pre rôzne užívateľské funkcie zvyčajne vytvoríme jednotlivé funkcie v zdrojovom kóde. V praxi sa nám bude často stávať, že si budeme tvoriť okrem týchto funkcií ešte nejaké pomocné, napr. Môžeme mať funkciu pre výpis menu aplikácie alebo rozdelíme nejaký zložitý výpočet do viacerých menších funkcií kvôli prehľadnosti.

Funkciám sa niekedy hovorí podprogramy alebo subrutiny. Ak funkcia nevracia žiadnu hodnotu (viď ďalej), môže sa ju v niektorých jazykoch hovoriť procedúra. U väčších aplikácií, ktoré majú mnoho funkcií, sa funkcia združujú do tzv. Modulov. Tie vy dobre poznáte napr. V podobe #include <stdio.h>, ktorým načítame knižnicu (modul) pre prácu so štandardným vstupom a výstupom (teda pre nás s konzolou). Podobne sú matematické funkcie sústredené v systémovom module math.h. Tieto moduly alebo ak knižnice sa tiež naučíme vytvárať.

Tvorba funkcií

Funkcia je logický blok kódu, ktorý raz napíšeme a potom ho môžeme ľubovoľne volať bez toho, aby sme ho písali znovu a opakovali sa. Funkciu deklarujeme v globálnom priestore, niekde nad funkciou main(). Bude vyzerať podobne. Pridajme do nášho zdrojového kódu funkciu, ktorá do konzoly vypíše " Ahoj, vrelo ťa tu vítam! ". Pre názornosť si prvýkrát uveďme kompletný zdrojový kód programu:

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

void pozdrav(void)
{
    printf("Ahoj, vrelo ťa tu vítam!\n");
}

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

Prvé slovo void v definícii funkcie udáva, že funkcia nevracia žiadnu hodnotu. Druhé void má podobný význam, určuje, že funkcia nemá žiadne vstupné parametre. Funkciu teraz musíme zavolať, aby sa spustila. Musíme to samozrejme urobiť až potom, čo ju deklarujeme, inak by ju kompilátor nepoznal (preto sme ju písali nad funkciu main()). Do main() napíšeme tento riadok:

pozdrav(); // zavolanie funkcie

výsledok:

Konzolová aplikácia
Ahoj, vrelo ťa tu vítam!

Funkcie s parametrami

Funkcia môže mať tiež ľubovoľný počet vstupných parametrov (niekedy sa im hovorí argumenty), ktoré píšeme do zátvorky v jej definícii. Parametre ovplyvňujeme správanie funkcie. Majte situácii, keď chceme pozdraviť nášho používateľa podľa mena. Rozšírime teda existujúce funkciu o parameter jmeno a ten potom pridáme s konkrétnou hodnotou do volanie funkcie:

void pozdrav(char jmeno[])
{
    printf("Ahoj, vrelo ťa tu vítam %s!\n", jmeno);
}

Volanie funkcie v main() následne upravíme takto:

pozdrav("Karle"); // zavolanie funkcie

Keby sme teraz chceli pozdraviť niekoľko ľudí, nemusíme otrocky písať znova a znova printf("Ahoj, vřele..., stačí nám len viackrát zavolať našej funkcii:

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

výsledok:

Konzolová aplikácia
Ahoj, vrelo ťa tu vítam Karle!
Ahoj, vrelo ťa tu vítam Davide!
Ahoj, vrelo ťa tu vítam Mařenko!

Návratová hodnota funkcie

Funkcia môže ďalej vracať nejakú hodnotu. Opusťme náš príklad s pozdravom a vytvorme tentoraz funkciu, ktorá nám spočíta obsah obdĺžnika. Tento obsah však nebudeme chcieť len vypísať, ale budeme ho chcieť použiť v ďalších výpočtoch. Preto výsledok funkcie nevypíše, ale vrátime ako návratovú hodnotu. Funkcia môže vracať práve jednu hodnotu pomocou príkazu return, ktorý zároveň funkciu aj ukončí, ďalší kód za return teda už nebude spustený. Dátový typ návratovej hodnoty musíme uviesť pred definíciu funkcie. Pridajte si do programu nasledujúcej funkcii:

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

V praxi by naše funkcie samozrejme počítala niečo zložitejšieho, aby sa nám ju vyplatilo vôbec programovať. V príklade ale obdĺžnik poslúži dobre. Funkcia pomenovávame malými písmenami, celými slovami a miesto medzier používame podčiarknutia. Hoci céčko samotné je plné skrátené, vy sa im vyhnite. Je totiž oveľa čitateľnejšie funkcie datum_narozeni() ako datnar(), u ktorej nemusia byť na prvý pohľad zrejmé čo že to vôbec robí.

Ak by sme teraz chceli vypísať obsah nejakého obdĺžnika, jednoducho vložíme volanie funkcie do funkcie printf(). Ako prvý sa spočíta obsah obdĺžnika, funkcia túto hodnotu vráti a hodnota príde ako vstupný parameter funkciu printf(), ktorá ju vypíše. Ako šírku a výšku zadajte napr. 10 a 20 cm:

printf("Obsah obdĺžnika je: %d cm^2", obsah_obdelniku(10, 20));
Konzolová aplikácia
Obsah obdĺžnika je: 200 cm^2

Pokiaľ vám to príde zmätené, vždy môžete použiť ešte pomocnú premennú:

int obsah = obsah_obdelniku(10, 20);
printf("Obsah obdĺžnika je: %d cm^2", obsah);

Návratovú hodnotu funkcie sme však nepoužili kvôli tomu, aby sme ju len vypisovali. Využime teraz toho, že je výpis na nás, a výpisy súčet obsahov dvoch obdĺžnikov:

int celkovy_obsah = obsah_obdelniku(10, 20) + obsah_obdelniku(20, 40);
printf("Súčet obsahov obdĺžnika je: %d cm^2", celkovy_obsah);

výsledok:

Konzolová aplikácia
Súčet obsahov obdĺžnika je: 1000 cm^2

Spomeňme si na minulé príklady, ktoré sme počas nášho seriálu vytvorili. Môžete si ich skúsiť prepísať tak, aby ste volali funkciu. V rámci návrhu by všetok kód mal byť rozdelený do funkcií (a ideálne do modulov, viď. Ďalšie diely) a to najmä kvôli prehľadnosti. My sme to zo začiatku kvôli jednoduchosti zanedbali, teraz to prosím berte na vedomie :)

Výhoda funkciou je teda v prehľadnosti a úspornosti (môžeme napísať nejakú vec raz a volať ju treba stokrát na rôznych miestach programu). Keď sa rozhodneme funkciu zmeniť, vykonáme zmenu len na jednom mieste a táto zmena sa prejaví všade, čo značne znižuje riziko chýb. V príklade, kde zdravíme Karla, Davida a Marienku nám stačí zmeniť text pozdravu vo funkcii a zmení sa vo všetkých troch volaniach. Nemať kód vo funkcii, museli by sme prepisovať 3 vety a v nejakej by sme mohli urobiť chybu.

Rekurzia

Na koniec si urobme ešte odbočku k pokročilejšiemu téme, ktorým je rekurzia. Rekurzívne funkcie je taká funkcia, ktorá v tele volá sama seba. Takáto funkcia potrebuje nejakú informáciu, podľa ktorej spozná, kedy má skončiť (tzv. Ukončenie rekurzia), inak by zavolala seba, tá zas seba a tak až do pádu programu na nedostatok pamäti. Rekurzia sa často používa v algoritmizácia.

Vo funkcionálnych jazykoch sa rekurzia používa namiesto cyklov. Zoberme si napríklad cyklus for, ktorý je súčtom čísla od 1 do 10. Rovnaký výsledok môžeme docieliť aj rekurzia, funkcie sa buď zavolá znovu s číslom o 1 vyšším alebo sa ukončí.

int cyklus(int aktualni_index, int konecny_index, int suma)
{
    if (aktualni_index == konecny_index)
        return suma;
    return cyklus(aktualni_index + 1, konecny_index, suma + aktualni_index);
}

Funkciu by sme zavolali takto:

printf("%d", cyklus(0, 10, 0)); // začiatok rekurzia

To isté môžeme zapísať pomocou cyklu for:

// ekvivalentné zápis s for
int suma = 0;
int a;
for (a = 0; a < 10; a++)
    suma += a;
printf("%d", suma);

Ako môžete vidieť, čítanie rekurzia nie je tak jednoduché, ako je tomu u cykle for. Aby toho nebolo málo, použitie rekurzia sebou nesie dodatočnú záťaž, pretože sa musí opakovane odovzdávať parametre (viac v článku o kompiláciu). Všeobecne možno veľkú časť programov, ktoré používajú rekurziu, prepísať do podoby bez rekurzia. Pre príklad si napíšeme program, ktorý počíta faktoriál. Predvedieme si verziu s rekurzia a verziu bez rekurzia.

int faktorial(int x)
{
    if (x == 1)
        return 1;
    return x * faktorial(x - 1);
}

Funkciu by sme zavolali takto:

printf("%d", faktorial(10));

A alternatíva pomocou cyklu:

int vysledek = 1;
int x = 10;
int i;
for (i = 2; i <= x; i++)
    vysledek *= i;
printf("%d", vysledek);

S rekurzia sa môžete často stretnúť v už existujúcich programoch alebo na pohovoroch do práce. Avšak odporúčam sa rekurziu skôr vyhýbať, aspoň zo začiatku. Rekurzia tiež dokáže veľmi rýchlo zaplniť zásobník a ukončiť celý program. Naviac je zložitá na chápania, ak vás zmiatla, ešte sa s ňou stretnete minimálne u algoritmov, kde bude dostatok priestoru pre ďalšie vysvetlenie.

V budúcej lekcii, Riešené úlohy k 12. lekcii céčko , sa pozrieme na ďalšie sa základných aspektov C - na štruktúry.

V nasledujúcom cvičení, Riešené úlohy k 12. lekcii céčko, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Predchádzajúci článok
Riešené úlohy k 10. a 11. lekcii Céčka
Všetky články v sekcii
Základné konštrukcie jazyka C
Preskočiť článok
(neodporúčame)
Riešené úlohy k 12. lekcii céčko
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
1 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity