Obsah
Funkce může, ale také nemusí, vracet návratovou hodnotu. Funkce může, ale také nemusí, mít argumenty. Již tedy začínáme chápat, že základní stavební kámen C - funkce - poskytuje množství variant. V této části si doplníme znalosti o proměnných. Upřesníme si, jakým způsobem se vytvářejí a kam se umisťují. Vzápětí poté si ukážeme využití těchto nových informací. Vytvoříme funkci, která volá sama sebe - rekurzivní funkci.
Funkce představují základní programovou jednotku, která řeší nějaký problém. Pokud je problém příliš složitý, volá na pomoc další funkci či funkce. Z toho plyne, že by funkce neměla být příliš rozsáhlá. Pokud tomu tak je, stává se funkce nepřehlednou i obtížně modifikovatelnou.
Každý C program obsahuje alespoň jednu funkci - main(). ISO norma jazyka C určuje návratovou hodnotu funkce main() typu int. Co obsahuje tělo této funkce je věcí každého programátora. Ale je jisté, že v ní musí být zapsána nejméně kostra programu. Překladač jazyka C totiž určuje, že právě funkcí main() začíná provádění každého programu. Tato vlastnost činí popisovanou funkci výjimečnou. Jinak se ovšem jedná o funkci, kterou prostě musí programátor napsat.
Na záčátku této kapitoly si ještě ukážeme obecný formát programu s více funkcemi.
Funkce mohou mít samozřejmě jména. Položka návratový-typ představuje typ dat vracených funkcí. Nevrací-li funkce žádnou hodnotu měl by být její návratový typ void. Nepoužívá-li funkce argumenty, měl by její seznam-argumentů obsahovat klíčové slovo void.
Konkrétnější informace o funkcích si řekneme dále v textu.
Potřebné znalosti | |
K zvládnutí obsahu této kapitoly je nutné mít znalosti z kapitoly 2 až 5 - chápat jednotlivé prvky programů, orientovat se v problematice řízení chodu programu - příkazy if-else a switch. Chápat rozdíly a použítí cyklů for, while a do. |
Uživatelské funkce jsou funkce, které jsme napsali a máme jejich zdrojové texty. Je dobrým zvykem naše funkce precizně dokumentovat a archivovat. A začít můžeme komentáři ve zdrojovém textu. To vše patří k dobrému programátorskému stylu. Když už jsme funkci jednou napsali a odladili, můžeme ji příště s důvěrou používat. A právě dokumentace nám při použití pomůže.
Identifikátor funkce je prostě jméno, pod kterým se v programech budeme na funkci odvolávat. Současně se jménem funkce určujeme i datový typ návratové hodnoty funkce. |
Nejjednodušší způsob, jak vytvořit funkci, se nazývá definice funkce. Při definici funkci najednou pojmenujeme, určíme typ její návratové hodnoty, typ a názvy jejích argumentů a současně napíšeme kód, který bude při každém volání funkce proveden. Tento kód zapisujeme v těle funkce. Argumenty uvedené při definici funkce označujeme jako formální argumenty. Tyto formální argumenty zastupují v těle funkce argumenty, se kterými byla funkce volána. Těchto skutečných argumentů může být mnoho a jejich identifikátory se jistě budou lišit.
Syntaxe:
Tělo funkce umístěno v bloku. O bloku jsme se zmínili dříve. Vime tedy, že může obsahovat lokální proměnné. Po nich následuje posloupnost příkazů. Ty definují chování (vlastnosti) funkce. Při definici funkce vytváříme (definujeme) její kód.
Voláním funkce - říkáme, že chceme provést kód, který je funkcí definován. Při volání funkce musí být za jménem funkce vždy uvedeny závorky. V nich pak mohou být uvedeny argumenty, které při volání funkci předáváme. Pokud závorky za jménem funkce neuvedeme, jde o adresu funkce. Adresou funkce rozumíme adresu vstupního bodu funkce. V místě, odkud funkci voláme, jí předáváme skutečné argumenty, které představují identifikátory proměnných a konstant, nebo výrazy.
jméno(argumenty);
Když jsme funkci pojmenovávali, určili jsme přitom typ její návratové hodnoty. V těle funkce pak musíme určit, jakým způsobem návratovou hodnotu získáme. Návratová hodnota funkce musí být výsledkem výrazu uvedeného jako argument příkazu return. Typ tohoto výrazu se musí shodovat, nebo být alespoň slučitelný, s deklarovaným typem návratové hodnoty funkce.
Syntaxe návratové hodnoty:
Na typ funkce, přesněji typ její návratové hodnoty, nejsou kladena žádná omezení. Pokud nás návratová hodnota funkce nezajímá, tak ji prostě po volání nepoužijeme. Děláme to často, například funkce scanf() a printf() hodnotu vrací a my ji ne vždy použijeme. Pokud ovšem chceme deklarovat, že funkce nevrací žádnou hodnotu, pak použijeme klíčové slovo void.
V této chvíli si vyzkoušíme, zda jsme informace o funkcích pochopili správně. Vytvoříme program který na obrazovku vypíše číslice 1 2 3.
V tomto programu volá funkce main() nejprve funkce2(), která volá funkce1(). Po vyvolání funkce1() vypíše 1, předá řízení zpět funkce2(), která vypíše 2 a řízení předá zpět funkci main(), která vypíše 3.
Následující program vypíše druhou mocninu čísla zadaného z klávesnice. Druhá mocnina se získá pomocí funkce mocnina().
Příkaz return se v těle funkce nemusí vyskytovat pouze jedenkrát. Pokud to odpovídá větvení ve funkci, může se vyskytovat na konci každé větve. Rozhodně však příkaz return ukončí činnost funkce, umístí návratovou hodnotu na specifikované místo - programátor se o umístění obvykle nestará - a předá řízení programu bezprostředně za místem, z něhož byla funkce volána. Činnost funkce, jejíž návratový typ není void, musí vždy končit příkazem return. Funkce typu void končí rovněž dosažením konce bloku, který tvoří tělo funkce.
Před každý formální argument musíme při deklaraci či definici funkce uvést jeho datový typ. Současně s tím určujeme i způsob, jakým se budou hodnoty skutečných argumentů předávat argumentům formálním. Dále platí, že pořadí formálních a skutečných argumentů si odpovídá. Tedy že první skutečný argument je v těle funkce zastupován prvním formálním argumentem, druhý druhým, atd. Přirozeně se vyžaduje, aby jejich datové typy byly stejné, nebo alespoň slučitelné. Přesná pravidla uvádí norma ISO C.
Nyní si ukážeme jednoduchý program, který volá funkce a předává jim argumenty jak hodnotou tak adresou.
Funkce nacti() má vracet dvě načtené hodnoty. K tomu nemůžeme použít její návratovou hodnotu. Předáváme jí proto argumenty adresou. Pozor na & adresový operátor před skutečnými argumenty při volání! Bez něj adresy nezískáme. Ještě si však všimneme návratové hodnoty této funkce. Tato návratová hodnota slouží k tomu, aby potvrdila platnost (respektive neplatnost) hodnot argumentů, které předává adresou.
Ještě si ukažme jeden příklad. Pěkným příkladem volání odkazem bude funkce swap(), která zamění dvě celočíselné hodnoty.
Jelikož jsou funci předány ukazatele na dvě celá čísla, zamění se hodnoty, na které tyto ukazatele ukazují.
Při deklaraci proměnné můžeme nepovinně uvést i klíčové slovo, určující paměťovou třídu. Paměťová třída určuje překladači náš požadavek na umístění proměnné. Pokud paměťovou třídu neurčíme při deklaraci, platí implicitní pravidla pro určení paměťové třídy.
Tabulka 6.2.
|
Paměťovou třídu můžeme upřesnit ještě tak zvanou typovou částí. Jedná se o klíčové slovo const a klíčové slovo volatile. První možnost určuje neměnnost, zatímco druhá deklaruje její opak. Jak to chápeme? Představme si dvě současně běžící úlohy se sdíleným paměťovým prostorem. Pokud úlohy sdílejí nějakou společnou proměnnou, kterou navíc mění, nemůže procesor jakkoli optimalizovat přístup k této proměnné.
Následující tabulka uvádí nejdůležitější pravidla pro implicitní určení paměťové třídy. Tedy ukazuje, jakou paměťovou třídu budou proměnné jazyka C.
Rekurze je proces, ve kterém je něco definováno samo sebou. Aplikujeme-li to na počítačový jazyk, rekuze znamená, že funkce může volat sama sebe. Ne všechny programovací jazky podporují rekurzi. Jazyk C však ano.
Je důležité vědět, že neexistují vícenásobné kopie rekurzivní funce. Existuje pouze jediná. Když je volána funkce, vyhradí se v zásobníku místo pro její argumenty a lokální proměnné. Když je tedy funkce volána rekurzivně, začíná pracovat s novou sadou argumentů a lokálních proměnných, ale kód, který tvoří funkci, zůstává stejný.
Nejčastějším příkladem užití rekurze je výpočet faktoriálu. I my si ukážeme jak vypočíst faktoriál na základě rekurzivní funkce.
Příklad 6.5. | |
/****************************** * recurfac.c * 19.03.2002 ******************************/ #include <stdio.h> int fakt(int n) { return (( n <= 0 ) ? 1 : n * fakt(n-1)); } int main() { int i; printf("\nZadej cele cislo: "); scanf("%d",&i); printf("Faktorial cisla %d je: %d",i,fakt(i)); return 0; } |
Nyní si ukážeme ještě jeden jednoduchý program abychom rekurzi dostatečně porozuměli. Naším úkolem bude vypsat na obrazovku čísla od 9 do 0. A použijeme k tomu rekurzivní funkci.
Příklad 6.6. | |
/****************************** * recur9-0.c * 19.03.2002 ******************************/ #include <stdio.h> void rekurze(int i); int main(void) { rekurze(0); return 0; } void rekurze(int i) { if(i<10) { rekurze(i+1); printf("%d ",i); } } |
Krátké vysvětlení:
Nejprve je volána funkce rekurze() s argumentem 0. Jelikož je 0 menší než 10 pak volá sama sebe s hodnotou i + 1. To způsobí opětovné vyvolání funkce rekurze() tentokrát s argumentem 1. Tento proces se opakuje dokud není rekurze() volána s argumentem 10. To způsobí návrat z funkce rekurze(). Jelikož se funkce vrací na místo svého vyvolání, provede se příkaz printf() - vytiskne se číslo 9 a vrací se. Vrátí se do místa svojí předchozí aktivace a vytiskne 8. Atd.
Z běžného použití známe standardní funkce, uživatelské funkce, podpůrné a nádstavbové funkce. Nejjednodušší možné členění zní: funkce standardní a funkce ostatní.
Standardním funkcím se zpravidla říká knihovní funkce. Jejich deklarace je popsána ve standardních hlavičkových souborech. Deklaracím funkcí se někdy říká prototyp.
Norma ISO C vyžaduje prototyp každé funkce, kterou chceme použít. Tento požadavek výrazně zvyšuje bezpečnost - umožňuje typovou kontrolu. Proto musíme začleňovat hlavičkové soubory tehdy, když používáme standardní funkce - hlavičkové soubory obsahují jejich deklarace. U deklarací nevadí, zahrneme-li shodnou deklaraci funkce vícekrát. |
Shrnutí | |
Syntaxe deklarace funkce: typ jméno(seznam argumentů); kde:
Deklarace popisuje vstupy a výstupy, které funkce poskytuje, ale nedefinuje posloupnost příkazů, které má funkce vykonávat. Deklarace určuje rozhraní funkce. Funkce nemá provádět akce s jinými daty, než která jí předáme jako argumenty. Současně výstupy z funkce mají probíhat jen jako její návratová hodnota. Pokud se funkce nechová uvedeným způsobem říkáme, že má vedlejší účinky, efekty. Pokud uvedeme pouze definici funkce, na kterou se později v souboru odvoláváme, slouží tato definice současně jako deklarace. Definici smíme uvést jen jednou. Při případné druhé definici by překladač nevěděl, která z nich je platná. Deklarace funkcí se umisťují do hlavičkových souborů. V případě neshody deklarace a definice funkce ohlásí překladač chybu. |