-
Funkce jsou základem programování v C a C++. Funkce jsou částí kódu oddělené
od hlavního programu. Tyto části kódu jsou volány (prováděny), když je
zapotřebí provést jistou akci v programu. Např. můžeme mít funkci, která
přebírá dvě hodnoty, provede s nimi jisté výpočty a vrátí výsledek. Nebo
můžeme mít funkci přebírající řetězec, rozdělí jej a vrací část tohoto
řetězce. Nejjednodušší typ funkce nemá parametry a vrací void (což
znamená, že nevrací nic). Jiné funkce mohou mít jeden nebo více parametrů
a mohou vracet hodnotu. Každý program musí mít právě jednu funkci
se jménem main (nebo WinMain)
označující vstupní bod programu. Funkce jsou obvykle deklarovány jako prototypy
ve standardních nebo uživatelských hlavičkových souborech, nebo uvnitř
zdrojových souborů. Funkce jsou definovány ve zdrojových souborech programu
nebo ve zpřístupněných knihovnách. Nedefinující deklarace funkcí používají
formát funkčního prototypu poskytující překladači detailní informace o
parametrech a tím mu umožňují provést kontrolu počtu parametrů, kontrolu
typů parametrů a použití případných automatických převodů typů. Prototypy
funkcí se v C++ musí používat a doporučuje se je používat i v C.
V jazyku C mohla být funkce původně deklarována buď implicitně svým
výskytem ve funkčním volání, nebo explicitně zápisem:
typ fce();
kde typ určuje návratový typ funkce (implicitně int),
přičemž funkce může vracet libovolný typ s výjimkou pole nebo funkce. Tento
styl deklarací však neumožňuje, aby překladač vedle kontroly návratového
typu prováděl také kontrolu počtu a typů parametrů funkce. Tento problém
byl vyřešen zavedením prototypu funkce. Prototyp funkce zapisujeme takto:
typ fce(seznam-deklarací-parametrů);
Deklarace specifikují typ každého parametru. Překladač tyto informace
používá pro kontrolu správnosti volání funkce. V některých případech je
překladač rovněž schopen převést typ parametru na požadovaný typ. Podívejte
se na následující část programu:
long lmax(long v1,
long v2); // prototyp
void main(int argc,
char **argv)
{
int limit
= 32;
char ch =
'A';
long mval;
mval = lmax(limit,
ch);
// volání funkce
}
Protože překladač zná prototyp funkce lmax, jsou parametry funkce
(limit a ch) nejprve použitím standardních pravidel převedeny
na typ long (požadovaný typ parametrů) a teprve potom jsou předány
funkci. Bez použití prototypu, překladač nezná datové typy parametrů a
neprovádí žádný převod. Funkci jsou tedy předány limit a ch
jako typy int a char, což funkce lmax neočekává a
dostáváme se do problémů.
Prototypy funkcí také přispívají k lepší dokumentaci programu. Např.
funkce strcpy (kopírování řetězce znaků) má dva parametry: zdrojový
a cílový řetězec. Otázkou je, který je zdrojový a který je cílový? Prototyp
funkce
char *strcpy(char
*cil, const char *zdroj);
vše objasní. Jména parametrů v prototypu mají pouze dokumentační účel.
Prototypy funkcí zapisujeme v hlavičkových souborech nebo na začátku zdrojového
textu programu.
Definice funkce obsahuje tělo funkce, tj. kód, který se bude při volání
funkce provádět. Deklarace formálních parametrů mají stejný zápis jako
deklarace proměnných. V C++ je možno v deklaraci parametrů uvést implicitní
hodnoty parametrů. Parametry s implicitními hodnotami musí být posledními
paramery. V deklaracích parametrů je možné použít i modifikátor const.
Definice funkce je popsána na následujícím obrázku:
Pokud funkce nevrací žádné informace, pak její typ je void.
Funkce typu void mají příkaz return bez výrazu určujícího
vrácenou hodnotu.
Při volání funkce jsou skutečné parametry funkce uvedeny ve stejném
pořadí jako parametry v definici funkce. Počet parametrů musí souhlasit.
Typy parametrů musí být kompatibilní do té míry, aby mohly být převedeny.
Pokud uvedeme definici funkce dříve než funkci použijeme, pak prototyp
funkce není nutno uvádět.
-
Použití funkce ukazuje následující konzolová aplikace:
#include <iostream.h>
#include <conio.h>
#pragma hdrstop
//---------------------------------------------------------------------
#pragma argsused
int nasob(int, int);
void zobrazVysledek(int);
int main(int argc,
char **argv)
{
int x, y,
vysledek;
cout <<
endl << "Zadej první hodnotu: ";
cin >> x;
cout <<
"Zadej druhou hodnotu: ";
cin >> y;
vysledek =
nasob(x, y);
zobrazVysledek(vysledek);
cout <<
endl << endl << "Stiskni libovolnou klávesu ... ";
getch();
return 0;
}
int nasob(int x,
int y)
{
return x*y;
}
void zobrazVysledek(int
vysl)
{
cout <<
"Výsledek je: " << vysl << endl;
}
Tento program vyžaduje zadání dvou čísel, volá funkci nasob
k vynásobení zadaných hodnot a potom volá funkci zobrazVysledek
k zobrazení výsledku. Prototypy těchto funkcí jsou uvedeny na začátku programu
(před funkcí main). V prototypech je uveden pouze návratový typ,
jméno funkce a datové typy parametrů funkce. Toto je minimální požadavek
na deklaraci funkce. Je-li to zapotřebí, prototyp funkce může obsahovat
jména parametrů, které mohou být použity k dokumentování činnosti funkce.
Např. deklarace funkce nasob může být zapsána také takto:
int nasob(int prvniCislo,
int druheCislo);
V našem případě je jasné co funkce dělá a není zapotřebí žádná dokumentace.
Definice této funkce je také jednoduchá. Naše funkce může být volána několika
způsoby. Mohou ji být předány konstanty, hodnoty proměnných a výsledek
může být také předán jiné funkci. Následuje několik příkladů volání této
funkce:
vysledek = nasob(5,
2);
vysledek = nasob(x,
y);
zobrazVysledek(nasob(x,
5));
nasob(3, x);
V posledním příkladě vrácená hodnota není použita. Ignorování vrácené
hodnoty je v našem případě nesmyslné, ale v některých případech je to často
používané. Je mnoho funkcí provádějících jisté akce a vrácená hodnota indikuje
stav volání. Pokud tato hodnota není důležitá pro náš program, pak ji můžeme
ignorovat. Např. v našich programech jsme zatím ignorovali hodnotu vracenou
funkcí getch (ASCII hodnota stisknuté klávesy).
Funkce může volat jiné funkce. Funkce může také volat sama sebe. Toto
nazýváme rekurze.
-
Následující konzolová aplikace zjišťuje, zda zadané číslo je prvočíslo.
Zda se jedná o prvočíslo určuje funkce je_prvocislo. Tato funkce
používá pomocnou funkci zjišťující dělitelnost čísel. Obě tyto funkce mají
v programu uveden prototyp. Prostudujte si tento výpis, program vyzkoušejte
a snažte se pochopit jak jednotlivé funkce pracují.
int je_prvocislo(int);
int je_delitelno(int,
int);
int main(int argc,
char **argv)
{
int n;
cout <<
"Zadej přirozené číslo: ";
cin >> n;
cout <<
"číslo " << n << (je_prvocislo(n) ? " je" : " neni")
<< " prvočíslo." << endl;
getch();
return 0;
}
int je_prvocislo(int
n)
{
for (int i
= 2; i*i <= n; i++)
if (je_delitelno(n, i)) return 0;
return 1;
}
int je_delitelno(int
n, int m)
{
return !(n
% m);
}
-
Následující funkce určuje největší společný dělitel dvou přirozených čísel.
Použijte tuto funkci v konzolové aplikaci, kde přečtete čitatel a jmenovatel
zlomku a provedete případné zkrácení zlomku. V programu uveďte i prototyp
této funkce.
int nsd(int a, int
b)
{
int pom;
while (b)
{
pom = a % b;
a = b;
b = pom;
}
return a;
}
-
Napište funkci vypisující řádek obsahující n znaků z. n
a z jsou parametry funkce. Tuto funkci použijte v programu pro výpis
např. 50-ti znaků podtržení a 20-ti znaků hvězdiček.
-
Přejdeme opět k aplikacím GUI. Vytvoříme aplikaci, kde na formuláři budou
tři editační ovladače (vyprázdníme je a před ně umístíme texty ?První operand:?,
?Druhý operand:? a ?Výsledek:?) a tlačítka ?Součet?, ?Rozdíl?, ?Součin?
a ?Podíl?. Obsluha události stisku tlačítka ?Součet? bude tvořena příkazy:
int x, y;
x = Edit1->Text.ToInt();
y = Edit2->Text.ToInt();
Edit3->Text = AnsiString(x
+ y);
Vytvořte obsluhy i pro ostatní tlačítka a program vyzkoušejte.
-
Do předchozí aplikace přidáme ještě další editační ovladač, ve kterém budeme
počítat počet provedených výpočtů (vyprázdníme jej a před něj umístíme
text ?Počet provedených výpočtů:?). Pro počítání provedených výpočtů musíme
zavést globální proměnou, nazveme ji např. Pocet (globální proměnné
deklarujeme mimo funkce; je nutno ji deklarovat před první funkcí, ve které
ji používáme). Do všech obsluh stisknutí tlačítek přidáme příkazy:
Pocet++;
Edit4->Text = AnsiString(Pocet);
Bylo by vhodné naši proměnnou Pocet na počátku výpočtu vynulovat.
Můžeme to provést tak, že její deklaraci spojíme s inicializací, tzn. před
první obsluhu vložíme:
int Pocet = 0;
-
Jistě jste si povšimli, že když stisknete některé tlačítko a nemáte zadané
operandy je signalizována chyba. Na začátku obsluh stisku tlačítek budeme
testovat, zda oba operandy jsou zadány. Toto testování je nutno provádět
ve všech obsluhách stisku tlačítek a tedy vytvoříme funkci, která otestuje
editační ovladač zadaný parametrem funkce (pokud editační ovladač je prázdný
vrátí true, jinak vrátí false). Tato funkce bude vypadat
takto:
bool NeniHodnota(TEdit
*EditOvl){
if (EditOvl->Text
== "") {
EditOvl->Color = clRed;
EditOvl->Text = "Zadej hodnotu";
EditOvl->SetFocus();
return true;
}
else {
EditOvl->Color = clWindow;
return false;
}
}
Parametr funkce je typu ukazatel na TEdit,
což je typ editačního ovladače (všechny typy komponent začínají písmenem
T) a jako skutečný parametr tedy budeme moci použít Edit1
i Edit2. Pokud v editačním ovladači není zapsán
žádný text, je změněna barva ovladače na červenou a je zde vypsán text
?Zadej hodnotu. PříZadej EditOvl->SetFocus();
zajistí vybrání testovaného editačního ovladače a můžeme tedy do něj ihned
zapisovat hodnotu. Pokud editační ovladač není prázdný, pak je změněna
jeho barva na normální barvu okna (clWindow). Tuto funkci musíme ještě
vyvolat na začátku všech obsluh stisků tlačítek. Na začátek všech těchto
obsluh tedy přidáme příkaz:
if (NeniHodnota(Edit1)
|| NeniHodnota(Edit2)) return;
Proveďte výše uvedené změny pro všechna tlačítka a vyzkoušejte. Není
to ale ideální řešení, neboť nezajišťuje aby v editačním ovladači byla
zapsána číselná hodnota.
-
V další aplikaci se seznámíme s používáním komponenty Timer
(časovač - je ji možno použít k definování časového intervalu). Na formulář
umístíme tuto komponentu (je na stránce System Palety komponent).
Jedna z vlastností této komponenty určuje časový interval v milisekundách
(tato vlastnost se jmenuje Interval). Do programu
vložíme deklaraci globální proměnné X typu bool a vytvoříme
obsluhu události OnTimer časovače s příkazy:
if (X) Color = clRed;
else Color = clGreen;
X = !X;
Zjistěte, co provádí tato aplikace.
-
Vytvořte aplikaci, ve které se pokusíte simulovat házení hrací kostkou.
Každou sekundu generujte hod a jeho výsledek zobrazte na formuláři (číselně).
Inicializaci generátoru náhodných čísel proveďte v obsluze události OnCreate
formuláře.
Nové pojmy:
-
Funkce je část kódu (oddělená od hlavního programu), která provádí
jednoduché, dobře definované akce.
-
Parametr je hodnota předávaná funkci, která ji použije ve svých
operacích.
-
Prototyp je deklarace funkce, která je uvedena před definicí funkce.
-
Definice funkce uvádí příkazy tvořící funkci.
-
Volání funkce je vyvolání příkazů tvořících funkci.
-
Rekurze je proces, ve kterém funkce volá sama sebe.
-
Lokální proměnná je proměnná deklarovaná uvnitř funkce (existuje
pouze do návrazu z funkce).
-
Globální proměnná je proměnná deklarovaná mimo funkci (existuje
do ukončení programu).