Programovanie (2) v Jave
1-INF-166, letný semester 2023/24

Prednášky · Pravidlá · Softvér · Testovač
· Vyučujúcich predmetu možno kontaktovať mailom na adresách uvedených na hlavnej stránke. Hromadná mailová adresa zo zimného semestra v letnom semestri nefunguje.
· JavaFX: cesta k adresáru lib je v počítačových učebniach /usr/share/openjfx/lib.


Prednáška 4: Rozdiel medzi revíziami

Z Programovanie
Skočit na navigaci Skočit na vyhledávání
 
(18 medziľahlých úprav od 2 ďalších používateľov nie je zobrazených)
Riadok 1: Riadok 1:
<!--
 
 
== Oznamy ==
 
== Oznamy ==
  
* V ''pondelok 7. októbra'' bude v čase prednášky teoretické cvičenie, na ktorom sa bude (na papier) písať krátky test zameraný na praktické znalosti učiva preberaného na prvých troch prednáškach. Za tento test sa budú udeľovať body v rámci hodnotenia cvičení č. 2 (úspešní riešitelia testu pre pokročilých sa teda testu zúčastniť nemusia). Na teste bude povolené používať ťahák vo forme jedného obojstranne popísaného listu A4, žiadne ďalšie pomôcky však povolené nebudú.
+
Čo nás čaká v najbližších dňoch
-->
+
* V piatok doplnkové cvičenia
 +
** Povinné iba pre tých, ktorí v utorok nespravili úspešne rozcvičku
 +
** Príďte, ak potrebujete pomôcť s riešením úloh alebo máte iné otázky k predmetu
 +
* V pondelok 2.10. bude časť prednášky teoretické cvičenie, na ktorom bude krátky test
 +
** Test bude z prvých troch prednášok
 +
** Ak máte aspoň jedny cvičenia uznané z testu pre pokročilých, na test nemusíte ísť
 +
** Na teste bude povolené používať pero a ťahák vo forme jedného obojstranne popísaného listu A4
  
 
==Funkcie==
 
==Funkcie==
Riadok 11: Riadok 16:
 
* Funkcia vo všeobecnosti dostane niekoľko (aj nula) vstupných argumentov, ktoré môže pri svojom behu používať.
 
* Funkcia vo všeobecnosti dostane niekoľko (aj nula) vstupných argumentov, ktoré môže pri svojom behu používať.
 
* Funkcia tiež môže vrátiť výstupnú hodnotu.
 
* Funkcia tiež môže vrátiť výstupnú hodnotu.
* Ide teda o veľmi podobný koncept ako funkcie v matematike:  
+
* Ide teda o veľmi podobný koncept ako funkcie v matematike (ako napríklad sin(''x'')):  
 
** Oboje si možno predstaviť ako &bdquo;krabičku&rdquo;, ktorá na základe niekoľkých vstupných hodnôt vráti nejakú výstupnú hodnotu.
 
** Oboje si možno predstaviť ako &bdquo;krabičku&rdquo;, ktorá na základe niekoľkých vstupných hodnôt vráti nejakú výstupnú hodnotu.
 
** Funkcie v C/C++ ale môžu okrem vracania výstupných hodnôt vykonávať ľubovoľný kód, teda napríklad aj niečo vypisovať na konzolu a podobne.
 
** Funkcie v C/C++ ale môžu okrem vracania výstupných hodnôt vykonávať ľubovoľný kód, teda napríklad aj niečo vypisovať na konzolu a podobne.
 
Funkcie sú užitočné z viacerých dôvodov:
 
* Umožňujú vytvoriť &bdquo;skratku&rdquo; pre často používané časti kódu, ktoré tak nie je nutné zakaždým písať nanovo.
 
* Umožňujú používanie kusov kódu vytvorených iným programátorom. Napríklad sme sa už stretli s funkciou <tt>sqrt</tt>, ktorá vráti druhú odmocninu svojho vstupu.
 
* Logicky napísané funkcie umožňujú oddeliť očakávanú funkcionalitu od jej reálnej implementácie. Napríklad pri volaní <tt>sqrt</tt> nás implementácia tejto funkcie typicky nemusí zaujímať &ndash; stačí vedieť, že pre každé číslo na vstupe táto funkcia vráti jeho odmocninu.
 
  
 
=== Obvod trojuholníka bez použitia funkcií ===
 
=== Obvod trojuholníka bez použitia funkcií ===
Riadok 48: Riadok 48:
 
      
 
      
 
     /* Spocitaj dlzky jednotlivych stran: */
 
     /* Spocitaj dlzky jednotlivych stran: */
     double a = sqrt((Bx - Cx) * (Bx - Cx) + (By - Cy) * (By - Cy));
+
     double a = sqrt((Bx - Cx) * (Bx - Cx)  
     double b = sqrt((Ax - Cx) * (Ax - Cx) + (Ay - Cy) * (Ay - Cy));
+
                    + (By - Cy) * (By - Cy));
     double c = sqrt((Ax - Bx) * (Ax - Bx) + (Ay - By) * (Ay - By));
+
     double b = sqrt((Ax - Cx) * (Ax - Cx)  
 +
                    + (Ay - Cy) * (Ay - Cy));
 +
     double c = sqrt((Ax - Bx) * (Ax - Bx)  
 +
                    + (Ay - By) * (Ay - By));
 
      
 
      
 
     cout << "Obvod trojuholnika ABC: " << a + b + c;
 
     cout << "Obvod trojuholnika ABC: " << a + b + c;
Riadok 59: Riadok 62:
 
=== Obvod trojuholníka s použitím funkcie ===
 
=== Obvod trojuholníka s použitím funkcie ===
  
Videli sme teda, že na výpočet obvodu trojuholníka sa nám môže zísť funkcia počítajúca vzdialenosť medzi dvoma bodmi v rovine (dĺžka jednej strany trojuholníka je vzdialenosťou jeho koncových bodov). Tá môže v C/C++ vyzerať napríklad takto:
+
Videli sme teda, že na výpočet obvodu trojuholníka sa nám môže zísť funkcia počítajúca vzdialenosť medzi dvoma bodmi v rovine, t.j. dĺžku úsečky. Tá môže v C/C++ vyzerať napríklad takto:
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
double dist(double x1, double y1, double x2, double y2) {
+
double dlzka(double x1, double y1, double x2, double y2) {
 
     return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
 
     return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Týmto sme zadefinovali funkciu s názvom <tt>dist</tt> so štyrmi ''vstupnými argumentmi'' <tt>x1, y1, x2, y2</tt> typu <tt>double</tt>, reprezentujúcimi súradnice dvojice bodov v rovine. Pred samotný názov funkcie sme zadali ''návratový typ'' funkcie, ktorým je <tt>double</tt> &ndash; táto funkcia teda bude vracať na výstupe reálne čísla. Nakoniec sme zadefinovali samotné telo funkcie, tentokrát pozostávajúce z jediného špeciálneho príkazu <tt>return</tt>, ktorým funkcia vracia svoju výstupnú hodnotu.
+
Týmto sme zadefinovali funkciu s názvom <tt>dlzka</tt> so štyrmi ''vstupnými argumentmi'' <tt>x1, y1, x2, y2</tt> typu <tt>double</tt>, reprezentujúcimi súradnice dvojice bodov v rovine. Pred samotný názov funkcie sme zadali ''návratový typ'' funkcie, ktorým je <tt>double</tt> &ndash; táto funkcia teda bude vracať na výstupe reálne čísla. Nakoniec sme zadefinovali samotné telo funkcie, tentokrát pozostávajúce z jediného špeciálneho príkazu <tt>return</tt>, ktorým funkcia vracia svoju výstupnú hodnotu.
  
''Kompletný program na výpočet obvodu trojuholníka'' môže s použitím funkcie <tt>dist</tt> vyzerať napríklad takto:
+
Kompletný program na výpočet obvodu trojuholníka môže s použitím funkcie <tt>dlzka</tt> vyzerať napríklad takto:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
Riadok 75: Riadok 78:
 
using namespace std;
 
using namespace std;
  
/* Definicia funkcie dist: */
+
/* Definicia funkcie dlzka: */
double dist(double x1, double y1, double x2, double y2) {
+
double dlzka(double x1, double y1, double x2, double y2) {
 
     return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
 
     return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
 
}
 
}
Riadok 92: Riadok 95:
 
     // Spocitaj dlzky jednotlivych stran:
 
     // Spocitaj dlzky jednotlivych stran:
  
     // Volanie funkcie dist s argumentmi Bx, By, Cx, Cy
+
     // Volanie funkcie dlzka s argumentmi Bx, By, Cx, Cy
     double a = dist(Bx, By, Cx, Cy);
+
     double a = dlzka(Bx, By, Cx, Cy);
     // Volanie funkcie dist s argumentmi Ax, Ay, Cx, Cy
+
     // Volanie funkcie dlzka s argumentmi Ax, Ay, Cx, Cy
     double b = dist(Ax, Ay, Cx, Cy);
+
     double b = dlzka(Ax, Ay, Cx, Cy);
     // Volanie funkcie dist s argumentmi Ax, Ay, Bx, By
+
     // Volanie funkcie dlzky s argumentmi Ax, Ay, Bx, By
     double c = dist(Ax, Ay, Bx, By);
+
     double c = dlzka(Ax, Ay, Bx, By);
 
      
 
      
 
     cout << "Obvod trojuholnika ABC: " << a + b + c;
 
     cout << "Obvod trojuholnika ABC: " << a + b + c;
Riadok 103: Riadok 106:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Telo funkcie nemusí pozostávať iba z jediného príkazu. Napríklad ešte jednoduchšie by sme funkciu <tt>dist</tt> mohli napísať takto:
+
Telo funkcie nemusí pozostávať iba z jediného príkazu. Napríklad ešte jednoduchšie by sme funkciu <tt>dlzka</tt> mohli napísať takto:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
double dist(double x1, double y1,  
+
double dlzka(double x1, double y1,  
 
             double x2, double y2) {
 
             double x2, double y2) {
 
     double dx = x1 - x2;
 
     double dx = x1 - x2;
Riadok 113: Riadok 116:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
''Cvičenie:''
+
Cvičenie:
* V Pascale existuje funkcia <tt>sqr</tt>, ktorá na vstupe berie reálne číslo <tt>x</tt> a výstupom je toto číslo umocnené na druhú. Naprogramujte túto funkciu v C/C++ a použite ju na zjednodušenie funkcie <tt>dist</tt>.  
+
* V Pascale existuje funkcia <tt>sqr</tt>, ktorá na vstupe berie reálne číslo <tt>x</tt> a výstupom je toto číslo umocnené na druhú. Naprogramujte túto funkciu v C/C++ a použite ju na zjednodušenie funkcie <tt>dlzka</tt>.  
 
* Mohli by sme použiť aj <tt>pow(x,2)</tt> (treba <tt>#include <cmath></tt>), ale môže byť jednoduchšie zrátať <tt>x*x</tt>.
 
* Mohli by sme použiť aj <tt>pow(x,2)</tt> (treba <tt>#include <cmath></tt>), ale môže byť jednoduchšie zrátať <tt>x*x</tt>.
 +
 +
== Výhody funkcií ==
 +
 +
Funkcie sú užitočné z viacerých dôvodov:
 +
* Umožňujú vytvoriť &bdquo;skratku&rdquo; pre často používané časti kódu, ktoré tak nie je nutné zakaždým písať nanovo.
 +
* Umožňujú používanie kusov kódu vytvorených iným programátorom. Napríklad sme použili funkciu <tt>sqrt</tt>, ktorá vráti druhú odmocninu svojho vstupu.
 +
* Funkcie umožňujú rozdeliť písanie programu na menšie časti. Zvlášť sa môžeme sústrediť na telo programu, ktoré používa funkciu a zvlášť na implementáciu samotnej funkcie. Tieto dve časti môžu robiť aj rôzni programátori.
  
 
== Definícia funkcie ==
 
== Definícia funkcie ==
  
 
Definícia funkcie pozostáva z nasledujúcich častí:
 
Definícia funkcie pozostáva z nasledujúcich častí:
* '''Typ návratovovej hodnoty funkcie.''' Funkcia z úvodného príkladu napríklad vracia vzdialenosť bodov v rovine, teda hodnotu typu <tt>double</tt>. Podobne môžeme písať funkcie vracajúce iné návratové typy, napríklad <tt>int</tt>. Funkcie, ktoré nemajú vracať žiadnu hodnotu majú špeciálny návratový typ <tt>void</tt> &ndash; ide typicky o funkcie, ktoré len vykonajú určitú činnosť, napríklad vypísanie textu na konzolu a podobne.
+
* '''Typ návratovej hodnoty funkcie.''' Funkcia z úvodného príkladu napríklad vracia vzdialenosť bodov v rovine, teda hodnotu typu <tt>double</tt>. Podobne môžeme písať funkcie vracajúce iné návratové typy, napríklad <tt>int</tt>. Funkcie, ktoré nemajú vracať žiadnu hodnotu majú špeciálny návratový typ <tt>void</tt> &ndash; ide typicky o funkcie, ktoré len vykonajú určitú činnosť, napríklad vypísanie textu na konzolu a podobne.
* '''Identifikátor funkcie.''' Funkciu môžeme (v rámci určitých medzí) pomenovať prakticky ľubovoľne, rovnako ako pri premenných. Vhodné je však použiť názov, ktorý vystihuje úlohu danej funkcie (napríklad <tt>dist</tt> od angl. ''distance'').
+
* '''Identifikátor funkcie.''' Funkciu môžeme (v rámci určitých medzí) pomenovať prakticky ľubovoľne, rovnako ako pri premenných. Vhodné je však použiť názov, ktorý vystihuje úlohu danej funkcie (napríklad <tt>dlzka</tt>).
 
* '''Zoznam vstupných parametrov funkcie.''' V zátvorkách za názvom funkcie je zoznam typov a identifikátorov vstupných argumentov, ktoré funkcia očakáva. V úvodnom príklade funkcia očakáva súradnice dvoch bodov v rovine, teda štyri hodnoty typu <tt>double</tt>. Ak funkcia neočakáva žiaden vstupný argument, môžeme do zátvoriek napísať <tt>void</tt> alebo nechať zoznam argumentov prázdny.
 
* '''Zoznam vstupných parametrov funkcie.''' V zátvorkách za názvom funkcie je zoznam typov a identifikátorov vstupných argumentov, ktoré funkcia očakáva. V úvodnom príklade funkcia očakáva súradnice dvoch bodov v rovine, teda štyri hodnoty typu <tt>double</tt>. Ak funkcia neočakáva žiaden vstupný argument, môžeme do zátvoriek napísať <tt>void</tt> alebo nechať zoznam argumentov prázdny.
 
* '''Telo funkcie.''' Do zložených zátvoriek za definíciou funkcie píšeme postupnosť príkazov, ktoré má funkcia vykonať.
 
* '''Telo funkcie.''' Do zložených zátvoriek za definíciou funkcie píšeme postupnosť príkazov, ktoré má funkcia vykonať.
Riadok 132: Riadok 142:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Príklad č. 1: Súčet čísel od ''a'' po ''b'' ===
+
== Ďalšie príklady funkcií ==
  
Nasledujúca funkcia dostane dvojicu celých čísel ''a'', ''b'' a vráti súčet všetkých celých čísel od ''a'' po ''b'' vrátane.
+
=== Súčet čísel od ''a'' po ''b'' ===
 +
 
 +
Ukážeme si tri verzie funkcie, ktorá dostane dvojicu celých čísel ''a'', ''b'' a spočíta súčet všetkých celých čísel od ''a'' po ''b'' vrátane.
 +
* Prvá verzia tento súčet vráti ako návratovú hodnotu.
 +
* Druhá verzia súčet vypíše, vrátane sčitovaných čísel, jej návratový typ bude <tt>void</tt>.
 +
* Tretia verzia súčet aj vypíše aj vráti.
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
/* Funkcia, ktora spocita sucet a + (a+1) + ... + (b-1) + b */
+
/* Funkcia, ktora vrati sucet a + (a+1) + ... + (b-1) + b */
 
int sum(int a, int b) {
 
int sum(int a, int b) {
 
     int result = 0;
 
     int result = 0;
Riadok 145: Riadok 160:
 
     return result;
 
     return result;
 
}
 
}
</syntaxhighlight>
 
  
Program využívajúci túto funkciu môže vyzerať napríklad takto:
+
/* Funkcia, ktora vypise cisla a, (a+1), ..., b a ich sucet */
 
+
void printNumbers(int a, int b) {
<syntaxhighlight lang="C++">
 
#include <iostream>
 
using namespace std;
 
 
 
/* Funkcia, ktora spocita sucet a + (a+1) + ... + (b-1) + b */
 
int sum(int a, int b) {
 
 
     int result = 0;
 
     int result = 0;
 
     for (int i = a; i <= b; i++) {
 
     for (int i = a; i <= b; i++) {
 +
        if (i > a) {
 +
            cout << " + ";
 +
        }
 +
        cout << i;
 
         result += i;
 
         result += i;
 
     }     
 
     }     
    return result;
+
     cout << " = " << result << endl;
}
 
 
 
int main() {
 
    int a, b;
 
   
 
     cout << "Zadaj dvojicu celych cisel: ";
 
    cin >> a >> b;
 
   
 
    cout << "Sucet celych cisel od " << a << " po " << b << ": ";
 
    cout << sum(a, b) << endl;
 
 
}
 
}
</syntaxhighlight>
 
  
=== Príklad č. 2: Vypisovanie čísel od ''a'' po ''b'' ===
+
/* Funkcia, ktora vypise cisla a, (a+1), ..., b a ich sucet
 
+
* a tento sucet aj vrati. */
Predpokladajme teraz, že potrebujeme napísať funkciu <tt>printNumbers</tt>, ktorá čísla od ''a'' po ''b'' iba vypíše na výstup. Takáto funkcia teda nebude vracať žiadnu zmysluplnú hodnotu &ndash; jej návratový typ bude <tt>void</tt>. V ostatnom sa táto funkcia od funkcie z predošlého príkladu nebude príliš odlišovať.
 
 
 
<syntaxhighlight lang="C++">
 
/* Funkcia, ktora vypise cisla a, (a+1), ..., (b-1), b */
 
void printNumbers(int a, int b) {
 
    cout << "Cisla od " << a << " po " << b << ":";   
 
    for (int i = a; i <= b; i++) {
 
        cout << " " << i;
 
    }   
 
    cout << endl;
 
}
 
</syntaxhighlight>
 
 
 
''Kompletný program využívajúci túto funkciu'':
 
 
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
using namespace std;
 
 
 
/* Funkcia, ktora vypise cisla a, (a+1), ..., (b-1), b */
 
void printNumbers(int a, int b) {
 
    cout << "Cisla od " << a << " po " << b << ":";   
 
    for (int i = a; i <= b; i++) {
 
        cout << " " << i;
 
    }   
 
    cout << endl;
 
}
 
 
 
int main(void) {
 
    int a, b;
 
   
 
    cout << "Zadaj dvojicu celych cisel: ";
 
    cin >> a >> b;
 
   
 
    printNumbers(a, b);
 
 
 
    return 0;
 
}
 
</syntaxhighlight>
 
 
 
=== Príklad č. 3: Sčítanie a súčasné vypisovanie čísel ''a'' od ''b'' ===
 
 
 
Funkcie z predchádzajúcich dvoch príkladov je možné spojiť do jednej, ktorá tieto čísla vypíše a súčasne na výstupe vráti ich súčet:
 
 
 
<syntaxhighlight lang="C++">
 
/* Funkcia, ktora scita a vypise cisla a, (a+1), ..., (b-1), b */
 
 
int sumAndPrint(int a, int b) {
 
int sumAndPrint(int a, int b) {
 
     int result = 0;
 
     int result = 0;
    cout << "Cisla od " << a << " po " << b << ":";   
 
 
     for (int i = a; i <= b; i++) {
 
     for (int i = a; i <= b; i++) {
 +
        if (i > a) {
 +
            cout << " + ";
 +
        }
 +
        cout << i;
 
         result += i;
 
         result += i;
        cout << " " << i;
 
 
     }     
 
     }     
     cout << endl;
+
     cout << " = " << result << endl;
 
     return result;
 
     return result;
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
''Kompletný program využívajúci túto funkciu'':
+
Program využívajúci tieto funkcie môže vyzerať napríklad takto:
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
Riadok 239: Riadok 196:
 
using namespace std;
 
using namespace std;
  
/* Funkcia, ktora scita a vypise cisla a, (a+1), ..., (b-1), b */
+
// sem pridu definicie funkcii sum, printNUmbers, sumAndPrint  
int sumAndPrint(int a, int b) {
+
// uvedene vyssie
    int result = 0;
 
    cout << "Cisla od " << a << " po " << b << ":";   
 
    for (int i = a; i <= b; i++) {
 
        result += i;
 
        cout << " " << i;
 
    }   
 
    cout << endl;
 
    return result;
 
}
 
  
int main(void) {
+
int main() {
 
     int a, b;
 
     int a, b;
 
      
 
      
 
     cout << "Zadaj dvojicu celych cisel: ";
 
     cout << "Zadaj dvojicu celych cisel: ";
 
     cin >> a >> b;
 
     cin >> a >> b;
   
 
    int sum = sumAndPrint(a, b);
 
    cout << "Sucet cisel je: " << sum << endl;
 
   
 
    sumAndPrint(a,b); // Vystupnu hodnotu funkcie mozeme aj odignorovat.
 
  
     return 0;
+
     cout << "Test funkcie sum" << endl;
}
+
    cout << "Sucet celych cisel od " << a << " po " << b << ": ";
</syntaxhighlight>
+
    // zavolame funkciu a vysledok priamo vypiseme
 +
    cout << sum(a, b) << endl;
 +
    // vysledok funkcie mozeme ulozit do premennej
 +
    // a pouzit neskor:
 +
    int sucet = sum(a, b);
 +
    cout << "Druha mocnina suctu cisel je: " << sucet * sucet << endl;
  
=== Príklad č. 4: Fibonacciho čísla ===
+
    cout << endl << "Test funkcie printNumbers" << endl;
 
+
    // tato funkcia nema vysledok, nic neukladame
''[https://en.wikipedia.org/wiki/Fibonacci_number Fibonacciho postupnosť]'' je postupnosť čísel taká, že:
+
     printNumbers(a, b);
* Nultý člen je 0.
+
      
* Prvý člen je 1.
+
     cout << endl << "Test funkcie sumAndPrint" << endl;
* Každý ďalší člen postupnosti je daný súčtom dvoch predchádzajúcich.
+
     // dalsia funkcia vypise aj vrati hodnotu
 
+
     int sucet2 = sumAndPrint(a, b);
Jednotlivé členy tejto postupnosti sa nazývajú ''Fibonacciho čísla''. Prvých niekoľko členov Fibonacciho postupnosti je
+
     cout << "Druha mocnina suctu cisel je: " << sucet2 * sucet2 << endl;
 
 
:<math>0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, \ldots</math>
 
 
 
a ''n''-té Fibonacciho číslo ''F''(''n'') možno vypočítať pomocou nasledujúceho rekurentného vzťahu:
 
* ''F''(0) = 0,
 
* ''F''(1) = 1,
 
* Pre všetky prirodzené čísla ''n'' &ge; 2: ''F''(''n'') = ''F''(''n'' - 1) + ''F''(''n'' - 2).
 
 
 
Napíšeme teraz funkciu <tt>fibonacci</tt>, ktorá pre dané <tt>n</tt> vypočíta <tt>n</tt>-té Fibonnaciho číslo.
 
<syntaxhighlight lang="C++">
 
int fibonacci(int n) {
 
     if (n == 0) {
 
        return 0;
 
     } else if (n == 1) {
 
        return 1;
 
     } else {
 
        int F_posledne = 1;
 
        int F_predposledne = 0;
 
        for (int i = 2; i <= n; i++) {
 
            int F_n = F_posledne + F_predposledne;
 
            F_predposledne = F_posledne;
 
            F_posledne = F_n;                   
 
        }
 
        return F_posledne;
 
    }
 
}
 
</syntaxhighlight>
 
 
 
''Kompletný program využívajúci túto funkciu'':
 
 
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
using namespace std;
 
 
 
int fibonacci(int n) {
 
     if (n == 0) {
 
        return 0;
 
     } else if (n == 1) {
 
        return 1;
 
    } else {
 
        int F_posledne = 1;
 
        int F_predposledne = 0;
 
        for (int i = 2; i <= n; i++) {
 
            int F_n = F_posledne + F_predposledne;
 
            F_predposledne = F_posledne;
 
            F_posledne = F_n;                   
 
        }
 
        return F_posledne;
 
    }
 
}
 
 
 
int main(void) {
 
    int n;
 
     cout << "Zadaj n: ";
 
    cin >> n;
 
 
      
 
      
     cout << "F(" << n << ") = " << fibonacci(n) << endl;
+
     // vystupnu hodnotu funkcie mozeme aj odignorovat
 
+
    sumAndPrint(a,b);  
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
''Príklad behu programu'':
 
 
<pre>
 
Zadaj n: 9
 
F(9) = 34
 
</pre>
 
 
''Cvičenie:'' Napíšte funkciu <tt>printFibonacci</tt>, ktorá ''vypíše'' prvých ''n'' Fibonacciho čísel.
 
* Akú bude mať táto funkcia hlavičku?
 
* Vyskúšajte dva spôsoby implementácie: volaním funkcie <tt>fibonacci</tt> pre rôzne hodnoty <tt>n</tt> a prepísaním tejto funkcie, aby sa hodnoty vypisovali priamo počas ich výpočtu. Ktorý spôsob bude rýchlejší pre veľké <tt>n</tt> a prečo?
 
  
 
== Príkaz <tt>return</tt> ==
 
== Príkaz <tt>return</tt> ==
Riadok 362: Riadok 243:
 
}
 
}
  
int main(void) {
+
int main() {
 
     cout << f() << endl; // Vypise 1.
 
     cout << f() << endl; // Vypise 1.
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
* Funkcia s návratovým typom <tt>void</tt> môže tiež obsahovať príkaz <tt>return</tt>, avšak bez návratovej hodnoty za ním (t.j. bezprostredne nasledovaný bodkočiarkou). V takom prípade slúži iba na ukončenie vykonávania funkcie podobne, ako v nasledujúcom príklade, ktorý je ukážkou ''vyslovene zlého'' programátorského štýlu:
+
* Funkcia s návratovým typom <tt>void</tt> môže tiež obsahovať príkaz <tt>return</tt>, avšak bez návratovej hodnoty.
 +
** V takom prípade <tt>return</tt> slúži iba na ukončenie vykonávania funkcie, väčšinou je lepšie prepísať inak
 +
** Tu je ukážka, kde namiesto <tt>return</tt> by bolo lepšie použiť <tt>else</tt>:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
 
using namespace std;
 
using namespace std;
  
void f(int n) {
+
void akeCislo(int n) {
 
     if (n >= 0) {
 
     if (n >= 0) {
 
         cout << "Cislo je nezaporne." << endl;
 
         cout << "Cislo je nezaporne." << endl;
 
         return;
 
         return;
 
     }
 
     }
     cout << "Cislo je zaporne." << endl; // Vykona sa len v pripade n < 0.
+
 
 +
    // Vykona sa len v pripade n < 0:
 +
     cout << "Cislo je zaporne." << endl;  
 
}
 
}
  
int main(void) {
+
int main() {
 
     int n;
 
     int n;
 
     cin >> n;
 
     cin >> n;
     f(n);
+
     akeCislo(n);
    return 0;
 
 
}
 
}
 
</syntaxhighlight>  
 
</syntaxhighlight>  
* Napríklad minimum z dvoch čísel môžeme vypočítať dvoma rôznymi spôsobmi (ak z nejakého dôvodu nechceme použiť niektorú zo štandardných funkcií slúžiacich k tomuto účelu):
+
* Napríklad minimum z dvoch čísel môžeme vypočítať dvoma rôznymi spôsobmi:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
int minimum1(int a, int b) {
 
int minimum1(int a, int b) {
Riadok 407: Riadok 290:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
* Funkcie s návratovým typom iným ako <tt>void</tt> je žiadúce písať tak, aby na ľubovoľnom vstupe ich vykonávanie vždy skončilo príkazom <tt>return</tt>. Ak totiž takáto funkcia skončí inak, než príkazom <tt>return</tt>, jej výstupná hodnota je nedefinovaná (použije sa &bdquo;hocijaký nezmysel&rdquo;). Neskoršie volanie takýchto funkcií potom môže viesť k zákerným chybám. Definíciu funkcie s typom iným ako <tt>void</tt>, ktorá môže skončiť inak ako príkazom <tt>return</tt>, teda budeme považovať za chybu (a to aj v prípade &bdquo;istoty&rdquo;, že sa výstupné hodnoty tejto funkcie nebudú nikde používať; vtedy je totiž správnou voľbou návratový typ <tt>void</tt>).  
+
* Funkcie s návratovým typom iným ako <tt>void</tt> je žiadúce písať tak, aby na ľubovoľnom vstupe ich vykonávanie vždy skončilo príkazom <tt>return</tt>.  
 +
** Ak totiž takáto funkcia skončí inak, než príkazom <tt>return</tt>, jej výstupná hodnota je nedefinovaná (použije sa &bdquo;hocijaký nezmysel&rdquo;), čo môže viesť k zákerným chybám.  
  
''Cvičenie:'' Nájdite hodnotu nasledujúcej funkcie pre ''n=6'' a ''n=7''. Viete stručne popísať, čo funkcia robí pre všeobecné ''n''?
+
Cvičenie: Nájdite hodnotu nasledujúcej funkcie pre ''n=6'' a ''n=7''. Viete stručne popísať, čo funkcia robí pre všeobecné ''n''?
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
int zahada(int n) {
 
int zahada(int n) {
Riadok 425: Riadok 309:
 
Premenné v C/C++ možno rozdeliť na ''globálne'' a ''lokálne'':
 
Premenné v C/C++ možno rozdeliť na ''globálne'' a ''lokálne'':
 
* ''Globálne premenné'' možno používať vo všetkých funkciách, ktoré sú v programe definované za touto premennou.
 
* ''Globálne premenné'' možno používať vo všetkých funkciách, ktoré sú v programe definované za touto premennou.
* ''Lokálne premenné'' sú definované vo vnútri funkcie a môžu sa používať iba v rámci nej (alebo iba v rámci niektorej časti tejto funkcie, ak je táto premenná definovaná napríklad v tele cyklu a podobne).
+
* ''Lokálne premenné'' sú definované vo vnútri funkcie a môžu sa používať iba v rámci nej (alebo iba v rámci niektorej časti funkcie, ak je premenná definovaná napríklad v tele cyklu a podobne).
  
 
Platí navyše, že:
 
Platí navyše, že:
* Viaceré funkcie môžu mať lokálne premenné s tým istým názvom &ndash; každá funkcia potom samozrejme používa tú svoju.
+
* Viaceré funkcie môžu mať lokálne premenné s tým istým názvom &ndash; každá funkcia potom používa tú svoju. Toto sa bežne používa.
* Ak má lokálna premenná rovnaký názov ako nejaká globálna premenná, lokálna premenná ''prekryje'' globálnu &ndash; funkcia teda používa svoju lokálnu premennú (bližšia košeľa ako kabát).
+
* Ak má lokálna premenná rovnaký názov ako nejaká globálna premenná, lokálna premenná ''prekryje'' globálnu &ndash; funkcia teda používa svoju lokálnu premennú (bližšia košeľa ako kabát). Toto radšej nerobte, môže vzniknúť chaos.
  
 
Je silno odporúčané ''používať predovšetkým lokálne premenné''. Väčšie programy s globálnymi premennými môžu byť veľmi neprehľadné.
 
Je silno odporúčané ''používať predovšetkým lokálne premenné''. Väčšie programy s globálnymi premennými môžu byť veľmi neprehľadné.
 
=== Príklad č. 1: Program s globálnou premennou <tt>x</tt> ===
 
  
 
Nasledujúci program obsahuje globálnu premennú <tt>x</tt> a v každej funkcii lokálnu premennú s názvom <tt>y</tt>:
 
Nasledujúci program obsahuje globálnu premennú <tt>x</tt> a v každej funkcii lokálnu premennú s názvom <tt>y</tt>:
Riadok 447: Riadok 329:
 
}
 
}
  
int main(void) {
+
int main() {
 
     x = 10;
 
     x = 10;
 
     int y = 20;
 
     int y = 20;
Riadok 453: Riadok 335:
 
     f();                            // Vypise 10 10.   
 
     f();                            // Vypise 10 10.   
 
     cout << x << " " << y << endl;  // Vypise 10 20.
 
     cout << x << " " << y << endl;  // Vypise 10 20.
   
 
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
=== Príklad č. 2: Prekrývanie globálnych premenných lokálnymi ===
 
 
Nasledujúci program demonštruje mechanizmus prekrývania globálnych premenných lokálnymi.
 
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
using namespace std;
 
 
/* Z nasledujucej funkcie globalnu premennu n pouzivat nemozeme, lebo je
 
* v programe definovana az neskor: */
 
void f1(void) {
 
    // n = 1;
 
}
 
 
int n = -1;    // Premennu n inicializujeme na hodnotu -1.
 
 
void f2(void) {
 
    n = 2;    // Nastavi hodnotu globalnej premennej n na 2.
 
}
 
 
void f3(void) {
 
    int n;    // Lokalna premenna prekryje globalnu.
 
    n = 3;    // Meni len lokalnu premennu bez vplyvu na globalnu premennu n.
 
}
 
 
int main(void) {
 
    cout << n << endl; // Vypise -1.
 
    f2();     
 
    cout << n << endl; // Vypise 2.
 
    f3();     
 
    cout << n << endl; // Vypise 2.
 
   
 
    return 0;
 
}</syntaxhighlight>
 
  
 
== Parametre funkcií ==
 
== Parametre funkcií ==
Riadok 509: Riadok 354:
 
sa parametru <tt>a</tt> priradí hodnota 1 a parametru <tt>b</tt> sa priradí hodnota 2. Tieto sa ďalej správajú ako lokálne premenné funkcie <tt>f</tt>. Možno ich teda meniť, ale táto zmena sa neprejaví na mieste, odkiaľ funkciu voláme. Tento mechanizmus nazývame ''odovzdávaním parametrov hodnotou''.
 
sa parametru <tt>a</tt> priradí hodnota 1 a parametru <tt>b</tt> sa priradí hodnota 2. Tieto sa ďalej správajú ako lokálne premenné funkcie <tt>f</tt>. Možno ich teda meniť, ale táto zmena sa neprejaví na mieste, odkiaľ funkciu voláme. Tento mechanizmus nazývame ''odovzdávaním parametrov hodnotou''.
  
''Príklad'':
+
Príklad:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
Riadok 519: Riadok 364:
 
}
 
}
  
int main(void) {
+
int main() {
 
     int n = 1;
 
     int n = 1;
     f(n);              // Vypise 2.
+
     f(n);              // Vypise 2
     cout << n << endl;  // Vypise 1.
+
     cout << n << endl;  // Vypise 1
   
 
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 532: Riadok 375:
 
V prípade, že pred názov niektorého parametra v hlavičke funkcie napíšeme <tt>&</tt>, parameter sa bude ''odovzdávať referenciou''.  
 
V prípade, že pred názov niektorého parametra v hlavičke funkcie napíšeme <tt>&</tt>, parameter sa bude ''odovzdávať referenciou''.  
 
* Za takýto parameter možno pri volaní funkcie dosadiť iba premennú (kým pri odovzdávaní hodnotou môžeme použiť napríklad aj konštanty alebo iný výraz).
 
* Za takýto parameter možno pri volaní funkcie dosadiť iba premennú (kým pri odovzdávaní hodnotou môžeme použiť napríklad aj konštanty alebo iný výraz).
* Namiesto samotnej hodnoty sa funkcii pošle adresa premennej v pamäti (referencia).
+
* Namiesto hodnoty sa funkcii pošle adresa premennej v pamäti (referencia).
 
* Funkcia potom bude túto premennú používať pod novým názvom; jej prípadné zmeny sa prejavia aj na mieste, odkiaľ bola funkcia volaná.
 
* Funkcia potom bude túto premennú používať pod novým názvom; jej prípadné zmeny sa prejavia aj na mieste, odkiaľ bola funkcia volaná.
  
''Príklad'':
+
Príklad:
 
 
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <iostream>
Riadok 546: Riadok 388:
 
}
 
}
  
int main(void) {
+
int main() {
 
     int n = 1;
 
     int n = 1;
 
     f(n);              // Vypise 2.
 
     f(n);              // Vypise 2.
 
     cout << n << endl;  // Vypise 2.
 
     cout << n << endl;  // Vypise 2.
   
 
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Príklad č. 1: Viac ako jedna návratová hodnota ===
+
Ďalej si ukážeme niekoľko použití odovzdávania parametrov referenciou.
 +
 
 +
=== Viac ako jedna návratová hodnota ===
  
 
Odovzdávanie parametra referenciou používame napríklad vtedy, keď potrebujeme vrátiť viac ako jednu výstupnú hodnotu.
 
Odovzdávanie parametra referenciou používame napríklad vtedy, keď potrebujeme vrátiť viac ako jednu výstupnú hodnotu.
Napríklad nasledujúca funkcia <tt>mid</tt> dostane súradnice dvoch bodov <tt>[x1,y1]</tt> a <tt>[x2,y2]</tt> a do parametrov <tt>[xm,ym]</tt>, ktoré sú odovzdané referenciou, uloží súradnice stredu úsečky spájajúcej body <tt>[x1,y1]</tt> a <tt>[x2,y2]</tt>.
+
 
 +
Napríklad nasledujúca funkcia <tt>stred</tt> dostane súradnice dvoch bodov <tt>[x1,y1]</tt> a <tt>[x2,y2]</tt> a do parametrov <tt>[xm,ym]</tt>, ktoré sú odovzdané referenciou, uloží súradnice stredu úsečky spájajúcej body <tt>[x1,y1]</tt> a <tt>[x2,y2]</tt>.
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
Riadok 564: Riadok 407:
 
using namespace std;
 
using namespace std;
  
void mid(double x1, double y1, double x2, double y2, double &xm, double &ym) {
+
void stred(double x1, double y1, double x2, double y2,  
 +
          double &xm, double &ym) {
 
     xm = (x1 + x2) / 2;
 
     xm = (x1 + x2) / 2;
 
     ym = (y1 + y2) / 2;
 
     ym = (y1 + y2) / 2;
 
}
 
}
  
int main(void) {
+
int main() {
 
     double Ax, Ay, Bx, By;
 
     double Ax, Ay, Bx, By;
 
      
 
      
Riadok 578: Riadok 422:
 
      
 
      
 
     double Mx, My;
 
     double Mx, My;
     mid(Ax, Ay, Bx, By, Mx, My);
+
     stred(Ax, Ay, Bx, By, Mx, My);
     cout << "Stred usecky AB je [" << Mx << ", " << My << "]." << endl;
+
     cout << "Stred usecky AB je ["  
       
+
        << Mx << ", " << My << "]." << endl;  
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Príklad č. 2: Funkcia <tt>swap</tt> ===
+
=== Funkcia <tt>swap</tt> ===
  
Typickým príkladom na použitie odovzdávania referenciou je funkcia <tt>swap</tt>, ktorá vymení hodnoty dvoch premenných, ktoré dostane ako parametre.
+
Typickým príkladom použitia odovzdávania parametrov referenciou je funkcia <tt>swap</tt>, ktorá vymení hodnoty dvoch premenných, ktoré dostane ako parametre.
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
Riadok 599: Riadok 442:
 
}
 
}
  
int main(void) {
+
int main() {
 
     int x, y;
 
     int x, y;
 
     cin >> x >> y;
 
     cin >> x >> y;
Riadok 606: Riadok 449:
 
      
 
      
 
     cout << "x = " << x << ", y = " << y << endl;  
 
     cout << "x = " << x << ", y = " << y << endl;  
       
 
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 615: Riadok 456:
 
== Ošetrovanie chybných vstupov ==
 
== Ošetrovanie chybných vstupov ==
  
Občas môže nastať potreba, aby parametre funkcie spĺňali určité podmienky. V takom prípade je potrebné korektne sa vysporiadať aj so vstupmi, ktoré tieto podmienky nespĺňajú (aj keď túto skutočnosť budeme často zámerne ignorovať). Ukážeme si teraz zopár možných prístupov k tomuto problému.
+
Občas môže nastať potreba, aby parametre funkcie spĺňali určité podmienky. V takom prípade môže byť potrebné korektne sa vysporiadať aj so vstupmi, ktoré tieto podmienky nespĺňajú. Ukážeme si teraz zopár možných prístupov k tomuto problému.
  
 
=== Funkcia neošetrujúca chybné vstupy ===
 
=== Funkcia neošetrujúca chybné vstupy ===
Riadok 635: Riadok 476:
 
}
 
}
  
int main(void) {
+
int main() {
 
     int n;
 
     int n;
 
     cout << "Zadaj kladne cele cislo: ";
 
     cout << "Zadaj kladne cele cislo: ";
 
     cin >> n;
 
     cin >> n;
 
          
 
          
     cout << "Sucet delitelov " << n << ": " << sumOfDivisors(n) << "." << endl;
+
     cout << "Sucet delitelov " << n << ": "  
       
+
        << sumOfDivisors(n) << "." << endl;
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 648: Riadok 488:
 
Ak však používateľ zadá na vstupe nejaké záporné číslo, funkcia vždy vráti nulu, čo nie je úplne v súlade s očakávaním. Jednou možnosťou by samozrejme bolo prerobiť funkciu tak, aby pracovala správne aj na záporných vstupoch. Sú však aj situácie, keď podobné riešenie nie je možné. Ukážeme si preto ďalšie spôsoby, ako sa s nekorektným vstupom vysporiadať.  
 
Ak však používateľ zadá na vstupe nejaké záporné číslo, funkcia vždy vráti nulu, čo nie je úplne v súlade s očakávaním. Jednou možnosťou by samozrejme bolo prerobiť funkciu tak, aby pracovala správne aj na záporných vstupoch. Sú však aj situácie, keď podobné riešenie nie je možné. Ukážeme si preto ďalšie spôsoby, ako sa s nekorektným vstupom vysporiadať.  
  
=== Použitie funkcie <tt>exit</tt> ===
 
 
V prípade použitia nekorektného vstupu napríklad môžeme celý program ihneď ukončiť funkciou <tt>exit</tt> (treba <tt>#include <cstdlib></tt>). Parametrom tejto funkcie je návratová hodnota celého programu, ktorá typicky udáva, či program skončil korektne alebo nie. V našom prípade zrejme potrebujeme vyjadriť neúspešné ukončenie programu &ndash; ako parameter funkcie <tt>exit</tt> teda použijeme konštantu <tt>EXIT_FAILURE</tt>.
 
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <cstdlib>
 
using namespace std;
 
 
int sumOfDivisors(int n) {
 
    if (n <= 0) {
 
        exit(EXIT_FAILURE);   
 
    }
 
    int sum = 0;
 
    for (int i = 1; i <= n; i++) {
 
        if (n % i == 0) {
 
            sum += i;   
 
        }       
 
    } 
 
    return sum; 
 
}
 
 
int main(void) {
 
    int n;
 
    cout << "Zadaj kladne cele cislo: ";
 
    cin >> n;
 
       
 
    cout << "Sucet delitelov " << n << ": " << sumOfDivisors(n) << "." << endl;
 
       
 
    return 0;
 
}
 
</syntaxhighlight>
 
  
 
=== Použitie funkcie <tt>assert</tt> ===
 
=== Použitie funkcie <tt>assert</tt> ===
  
Pohodlnejšou alternatívou je použitie funkcie <tt>assert</tt> (treba <tt>#include <cassert></tt>). Táto funkcia umožňuje predpokladať platnosť nejakej podmienky &ndash; ak je táto podmienka splnená, program normálne pokračuje; v opačnom prípade sa program zastaví s chybovou hláškou. Argumentom funkcie <tt>assert</tt> môže byť ľubovoľná booleovská hodnota.
+
V prípade použitia nekorektného vstupu napríklad môžeme celý program ihneď ukončiť. Pohodlný spôsob, ako to spraviť, je použitie funkcie <tt>assert</tt> (treba <tt>#include <cassert></tt>). Táto funkcia kontroluje platnosť nejakej podmienky. Ak je táto podmienka splnená, program normálne pokračuje; v opačnom prípade sa program zastaví s chybovou hláškou. Argumentom funkcie <tt>assert</tt> môže byť ľubovoľná booleovská hodnota.
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
Riadok 701: Riadok 509:
 
}
 
}
  
int main(void) {
+
int main() {
 
     int n;
 
     int n;
 
     cout << "Zadaj kladne cele cislo: ";
 
     cout << "Zadaj kladne cele cislo: ";
 
     cin >> n;
 
     cin >> n;
 
          
 
          
     cout << "Sucet delitelov " << n << ": " << sumOfDivisors(n) << "." << endl;
+
     cout << "Sucet delitelov " << n << ": "  
       
+
        << sumOfDivisors(n) << "." << endl;
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 714: Riadok 521:
 
=== Úspech výpočtu ako výstupná hodnota ===
 
=== Úspech výpočtu ako výstupná hodnota ===
  
Často je ale neprípustné v prípade jediného volania funkcie s nekorektnými vstupmi ukončiť celý program. Elegantnejším riešením je preto napríklad nasledovné: súčet deliteľov už nebudeme vracať ako výstupnú hodnotu, ale budeme ho ukladať do parametra odovzdávaného referenciou. Výstupom funkcie bude namiesto toho booleovská hodnota, ktorá bude <tt>true</tt> práve vtedy, keď výpočet funkcie prebehol správne (t.j. keď boli zadané správne argumenty). Túto výstupnú hodnotu je potom možné použiť pri volaní funkcie ako známku toho, že hodnota v parametre predanom referenciou je zmysluplná.
+
Často je ale neprípustné v prípade jediného volania funkcie s nekorektnými vstupmi ukončiť celý program. Chceli by sme teda vrátiť dve hodnoty: samotný súčet deliteľov a indikátor, či boli argumenty zadané správne.
 +
* Napríklad súčet deliteľov môžeme ukladať do parametra odovzdávaného referenciou.  
 +
* Výstupom funkcie bude booleovská hodnota, ktorá bude <tt>true</tt> práve vtedy, keď výpočet funkcie prebehol správne (t.j. keď boli zadané správne argumenty). Túto výstupnú hodnotu je potom možné použiť pri volaní funkcie ako známku toho, že hodnota v parametre predanom referenciou je zmysluplná.
  
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
Riadok 734: Riadok 543:
 
}
 
}
  
int main(void) {
+
int main() {
 
     int n, sum;
 
     int n, sum;
 
     cout << "Zadaj kladne cele cislo: ";
 
     cout << "Zadaj kladne cele cislo: ";
Riadok 740: Riadok 549:
 
      
 
      
 
     if (sumOfDivisors(n, sum)) {  
 
     if (sumOfDivisors(n, sum)) {  
         cout << "Sucet delitelov " << n << ": " << sum << "." << endl;
+
         cout << "Sucet delitelov " << n << ": "  
 +
            << sum << "." << endl;
 
     } else {
 
     } else {
 
         cout << "Zly vstup" << endl;
 
         cout << "Zly vstup" << endl;
 
     }
 
     }
       
 
    return 0;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 751: Riadok 559:
 
== Programy s viacerými funkciami ==
 
== Programy s viacerými funkciami ==
  
V programe možno volať iba funkcie, ktoré už predtým boli niekde definované alebo aspoň deklarované (tento pojem si ozrejmíme o chvíľu). Napríklad
+
V programe možno volať iba funkcie, ktoré už predtým boli niekde definované.
 +
 
 +
Tento program typicky neskompiluje (vo vnútri <tt>f1</tt> ešte nepozná <tt>f2</tt>)
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
void f1(void) {
 
void f1(void) {
Riadok 761: Riadok 571:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
typicky neskompiluje, kým
+
 
 +
Tento program je v poriadku:
 
<syntaxhighlight lang="C++">
 
<syntaxhighlight lang="C++">
 
void f2(void) {
 
void f2(void) {
Riadok 769: Riadok 580:
 
void f1(void) {
 
void f1(void) {
 
     f2();
 
     f2();
}
 
</syntaxhighlight>
 
je v poriadku. Program tiež skompiluje v prípade, že každú funkciu pred jej volaním aspoň ''zadeklarujeme'' &ndash; t.j. uvedieme jej hlavičku bez definície jej tela &ndash; a samotnú definíciu funkcie uvedieme až neskôr. V poriadku je teda napríklad aj nasledujúci program:
 
<syntaxhighlight lang="C++">
 
void f2(void);
 
 
void f1(void) {
 
    f2();
 
}
 
 
void f2(void) {
 
    cout << "Hello, World!" << endl;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Riadok 786: Riadok 585:
 
== Funkcia <tt>main</tt> ==
 
== Funkcia <tt>main</tt> ==
  
Špeciálnou funkciou je v C/C++ funkcia <tt>main</tt>. Od ostatných funkcií sa odlišuje v nasledujúcom:
+
Špeciálnou funkciou je v C/C++ funkcia <tt>main</tt>.  
 
* V programe ju nemožno volať &ndash; jediné jej volanie je automatické (začína sa ním beh programu).
 
* V programe ju nemožno volať &ndash; jediné jej volanie je automatické (začína sa ním beh programu).
 
* Výstupná hodnota funkcie <tt>main</tt> sa interpretuje ako &bdquo;spôsob ukončenia&rdquo; programu (napríklad 0 pre korektné ukončenie, iné hodnoty pre chyby).
 
* Výstupná hodnota funkcie <tt>main</tt> sa interpretuje ako &bdquo;spôsob ukončenia&rdquo; programu (napríklad 0 pre korektné ukončenie, iné hodnoty pre chyby).
 +
** Funkciu <tt>main</tt> môžeme teda ukončit napríklad príkazom <tt>return 0</tt>.
 
* Funkcia <tt>main</tt> môže mať aj určité presne špecifikované parametre (viac o tom neskôr).
 
* Funkcia <tt>main</tt> môže mať aj určité presne špecifikované parametre (viac o tom neskôr).
  
Riadok 796: Riadok 596:
 
* V tele funkcie sú samotné príkazy. Vypočítanú hodnotu vrátime príkazom <tt>return</tt>.
 
* V tele funkcie sú samotné príkazy. Vypočítanú hodnotu vrátime príkazom <tt>return</tt>.
 
* Lokálne premenné sú viditeľné len vo funkcii, ktorá ich definuje, globálne vo všetkých funkciách.
 
* Lokálne premenné sú viditeľné len vo funkcii, ktorá ich definuje, globálne vo všetkých funkciách.
* Parametre odovzdávané hodnotou sú lokálne premenné inicializované určitou hodnotou, ktoré sa zadajú pri volaní funkcie.
+
* Parametre odovzdávané hodnotou sú lokálne premenné inicializované hodnotami, ktoré sa zadajú pri volaní funkcie.
 
* Parametre odovzdávané referenciou (<tt>&</tt>) sú len novým menom pre inú premennú.
 
* Parametre odovzdávané referenciou (<tt>&</tt>) sú len novým menom pre inú premennú.
 
<!--== Záznam typu struct ==
 
 
* V príkladoch s obvodom trojuholníka a stredom úsečky sme funkciám posielali veľa parametrov (súradnice x a y)
 
* Program bude krajší, ak si údaje o jednom bode spojíme do jedného záznamu
 
<syntaxhighlight lang="C++">
 
struct bod {
 
    double x, y;
 
};
 
</syntaxhighlight>
 
* Pomocou <tt>struct</tt> vytvoríme nový dátový typ <tt>bod</tt>, ktorý má zložky x a y
 
** V jednom struct-e môžu byť aj položky rôznych typov (napr. <tt>struct bod { double x,y; int id; bool visible; };</tt>)
 
* Môžeme vytvárať premenné typu <tt>bod</tt>, napr. <tt>bod a, b;</tt>
 
* K položkám bodu pristupujeme pomocou bodky, napr. <tt>a.x = 4.0; </tt>
 
* Do funkcií body posielame radšej referenciou, aby sa zbytočne nekopírovalo veľa hodnôt
 
 
Nasledujúci program načíta súradnice troch bodov, spočíta obvod trojuholníka a stredy všetkých troch strán.
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
#include <cmath>
 
using namespace std;
 
 
struct bod {
 
    double x, y;  // suradnice bodu v rovine
 
};
 
 
double dlzka(bod &bod1, bod &bod2) {
 
    // funkcia vrati dlzku usecky z bodu 1 do bodu 2
 
    double dx = bod1.x - bod2.x;
 
    double dy = bod1.y - bod2.y;
 
    return sqrt(dx * dx + dy * dy);
 
}
 
 
void stred(bod &bod1, bod &bod2, bod &stred) {
 
    // funkcia do bodu stred spocita stred usecky z bodu 1 do bodu 2
 
    stred.x = (bod1.x + bod2.x) / 2;
 
    stred.y = (bod1.y + bod2.y) / 2;
 
}
 
 
void vypisBod(bod &b) {
 
    // funkcia vypise surandice bodu v zatvorke a koniec riadku
 
    cout << "(" << b.x << "," << b.y << ")" << endl;
 
}
 
 
int main(void) {
 
    // nacitame suradnice vrcholov trojuholnika
 
    bod A, B, C;
 
    cout << "Zadaj suradnice vrcholu A oddelene medzerou: ";
 
    cin >> A.x >> A.y;
 
    cout << "Zadaj suradnice vrcholu B oddelene medzerou: ";
 
    cin >> B.x >> B.y;
 
    cout << "Zadaj suradnice vrcholu C oddelene medzerou: ";
 
    cin >> C.x >> C.y;
 
    // spocitame dlzky stran
 
    double da = dlzka(B, C);
 
    double db = dlzka(A, C);
 
    double dc = dlzka(A, B);
 
    // vypiseme obvod
 
    cout << "Obvod trojuholnika ABC: " << da + db + dc << endl;
 
 
    // spocitame stredy stran
 
    bod stredAB;
 
    stred(A, B, stredAB);
 
    bod stredAC;
 
    stred(A, C, stredAC);
 
    bod stredBC;
 
    stred(B, C, stredBC);
 
 
    // vypiseme stredy stran
 
    cout << "Stred strany AB: ";
 
    vypisBod(stredAB);
 
    cout << "Stred strany AC: ";
 
    vypisBod(stredAC);
 
    cout << "Stred strany BC: ";
 
    vypisBod(stredBC);
 
}
 
</syntaxhighlight>
 
 
Príklad behu programu:
 
<pre>
 
Zadaj suradnice vrcholu A oddelene medzerou: 0 0
 
Zadaj suradnice vrcholu B oddelene medzerou: 0 3
 
Zadaj suradnice vrcholu C oddelene medzerou: 4 0
 
Obvod trojuholnika ABC: 12
 
Stred strany AB: (0,1.5)
 
Stred strany AC: (2,0)
 
Stred strany BC: (2,1.5)
 
</pre>-->
 
 
<!-- == Spracovanie väčšieho množstva dát ==
 
 
Naše programy doteraz spracovávali len malý počet vstupných dát načítaných od užívateľa (napr. súradnice trocho bodov). Často však chceme pracovať s väčším množstvom dát
 
* Na budúcej prednáške si ukážeme, ako uložiť väčšie množstvo dát do poľa
 
* Na niektoré úlohy však pole nepotrebujeme - údaje môžeme spracovávať rovno ako ich užívateľ zadáva
 
 
V nasledujúcich príkladoch užívateľ zadá číslo ''N'' a potom ''N'' celých čísel
 
* Predstavme si napríklad, že učiteľ zadá body, ktoré študenti dostali na písomke (napr. celé čísla v rozsahu 0..10)
 
* Z týchto bodov chceme spočítať nejaké štatistiky
 
 
=== Priemer ===
 
<syntaxhighlight lang="C++">
 
#include <iostream>
 
using namespace std;
 
 
int main(void) {
 
    int N;
 
    cout << "Zadaj pocet cisel: ";
 
    cin >> N;
 
 
    int sucet = 0;
 
    cout << "Zadavaj cisla: ";
 
    for (int i = 0; i < N; i++) {
 
        int x;
 
        cin >> x;
 
        sucet += x;
 
    }
 
 
    double priemer = sucet / (double) N;
 
    cout << "Priemer je " << priemer << "." << endl;
 
}
 
</syntaxhighlight>
 
 
* Čo by sa stalo, keby sme vo výpočte priemeru vynechali <tt>(double)</tt>?-->
 

Aktuálna revízia z 21:00, 26. september 2023

Oznamy

Čo nás čaká v najbližších dňoch

  • V piatok doplnkové cvičenia
    • Povinné iba pre tých, ktorí v utorok nespravili úspešne rozcvičku
    • Príďte, ak potrebujete pomôcť s riešením úloh alebo máte iné otázky k predmetu
  • V pondelok 2.10. bude časť prednášky teoretické cvičenie, na ktorom bude krátky test
    • Test bude z prvých troch prednášok
    • Ak máte aspoň jedny cvičenia uznané z testu pre pokročilých, na test nemusíte ísť
    • Na teste bude povolené používať pero a ťahák vo forme jedného obojstranne popísaného listu A4

Funkcie

Funkcia je samostatný kus kódu (postupnosť príkazov) s určitým menom. Po zavolaní funkcie jej menom sa daná postupnosť príkazov vykoná.

  • V iných programovacích jazykoch sa používajú aj podobné termíny ako procedúra, metóda, či podprogram.
  • Funkcia vo všeobecnosti dostane niekoľko (aj nula) vstupných argumentov, ktoré môže pri svojom behu používať.
  • Funkcia tiež môže vrátiť výstupnú hodnotu.
  • Ide teda o veľmi podobný koncept ako funkcie v matematike (ako napríklad sin(x)):
    • Oboje si možno predstaviť ako „krabičku”, ktorá na základe niekoľkých vstupných hodnôt vráti nejakú výstupnú hodnotu.
    • Funkcie v C/C++ ale môžu okrem vracania výstupných hodnôt vykonávať ľubovoľný kód, teda napríklad aj niečo vypisovať na konzolu a podobne.

Obvod trojuholníka bez použitia funkcií

Užitočnosť funkcií ilustrujeme na príklade. Chceme napísať program, ktorý od používateľa načíta súradnice vrcholov trojuholníka ABC a na výstup vypíše obvod tohto trojuholníka. Beh takéhoto programu teda môže vyzerať napríklad takto:

Zadaj suradnice vrcholu A: 0 0
Zadaj suradnice vrcholu B: 3 0
Zadaj suradnice vrcholu C: 0 4
Obvod trojuholnika ABC: 12

Obvod spočítame ako súčet dĺžok jednotlivých strán, v programe teda trikrát opakujeme výpočet dĺžky strany:

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    double Ax, Ay, Bx, By, Cx, Cy;
    
    cout << "Zadaj suradnice vrcholu A: "; 
    cin >> Ax >> Ay;
    cout << "Zadaj suradnice vrcholu B: ";
    cin >> Bx >> By;
    cout << "Zadaj suradnice vrcholu C: ";
    cin >> Cx >> Cy;
    
    /* Spocitaj dlzky jednotlivych stran: */
    double a = sqrt((Bx - Cx) * (Bx - Cx) 
                    + (By - Cy) * (By - Cy));
    double b = sqrt((Ax - Cx) * (Ax - Cx) 
                    + (Ay - Cy) * (Ay - Cy));
    double c = sqrt((Ax - Bx) * (Ax - Bx) 
                    + (Ay - By) * (Ay - By));
    
    cout << "Obvod trojuholnika ABC: " << a + b + c;
}

Opakované písanie toho istého vzorca na výpočet dĺžky strany je prácne; navyše pri ňom ľahko spravíme chybu. V nasledujúcom programe ho teda nahradíme funkciou.

Obvod trojuholníka s použitím funkcie

Videli sme teda, že na výpočet obvodu trojuholníka sa nám môže zísť funkcia počítajúca vzdialenosť medzi dvoma bodmi v rovine, t.j. dĺžku úsečky. Tá môže v C/C++ vyzerať napríklad takto:

double dlzka(double x1, double y1, double x2, double y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

Týmto sme zadefinovali funkciu s názvom dlzka so štyrmi vstupnými argumentmi x1, y1, x2, y2 typu double, reprezentujúcimi súradnice dvojice bodov v rovine. Pred samotný názov funkcie sme zadali návratový typ funkcie, ktorým je double – táto funkcia teda bude vracať na výstupe reálne čísla. Nakoniec sme zadefinovali samotné telo funkcie, tentokrát pozostávajúce z jediného špeciálneho príkazu return, ktorým funkcia vracia svoju výstupnú hodnotu.

Kompletný program na výpočet obvodu trojuholníka môže s použitím funkcie dlzka vyzerať napríklad takto:

#include <iostream>
#include <cmath>
using namespace std;

/* Definicia funkcie dlzka: */
double dlzka(double x1, double y1, double x2, double y2) {
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

int main() {
    double Ax, Ay, Bx, By, Cx, Cy;
    
    cout << "Zadaj suradnice vrcholu A: "; 
    cin >> Ax >> Ay;
    cout << "Zadaj suradnice vrcholu B: ";
    cin >> Bx >> By;
    cout << "Zadaj suradnice vrcholu C: ";
    cin >> Cx >> Cy;
    
    // Spocitaj dlzky jednotlivych stran:

    // Volanie funkcie dlzka s argumentmi Bx, By, Cx, Cy
    double a = dlzka(Bx, By, Cx, Cy);
    // Volanie funkcie dlzka s argumentmi Ax, Ay, Cx, Cy
    double b = dlzka(Ax, Ay, Cx, Cy);
    // Volanie funkcie dlzky s argumentmi Ax, Ay, Bx, By
    double c = dlzka(Ax, Ay, Bx, By);
    
    cout << "Obvod trojuholnika ABC: " << a + b + c;
}

Telo funkcie nemusí pozostávať iba z jediného príkazu. Napríklad ešte jednoduchšie by sme funkciu dlzka mohli napísať takto:

double dlzka(double x1, double y1, 
            double x2, double y2) {
    double dx = x1 - x2;
    double dy = y1 - y2; 
    return sqrt(dx * dx + dy * dy);
}

Cvičenie:

  • V Pascale existuje funkcia sqr, ktorá na vstupe berie reálne číslo x a výstupom je toto číslo umocnené na druhú. Naprogramujte túto funkciu v C/C++ a použite ju na zjednodušenie funkcie dlzka.
  • Mohli by sme použiť aj pow(x,2) (treba #include <cmath>), ale môže byť jednoduchšie zrátať x*x.

Výhody funkcií

Funkcie sú užitočné z viacerých dôvodov:

  • Umožňujú vytvoriť „skratku” pre často používané časti kódu, ktoré tak nie je nutné zakaždým písať nanovo.
  • Umožňujú používanie kusov kódu vytvorených iným programátorom. Napríklad sme použili funkciu sqrt, ktorá vráti druhú odmocninu svojho vstupu.
  • Funkcie umožňujú rozdeliť písanie programu na menšie časti. Zvlášť sa môžeme sústrediť na telo programu, ktoré používa funkciu a zvlášť na implementáciu samotnej funkcie. Tieto dve časti môžu robiť aj rôzni programátori.

Definícia funkcie

Definícia funkcie pozostáva z nasledujúcich častí:

  • Typ návratovej hodnoty funkcie. Funkcia z úvodného príkladu napríklad vracia vzdialenosť bodov v rovine, teda hodnotu typu double. Podobne môžeme písať funkcie vracajúce iné návratové typy, napríklad int. Funkcie, ktoré nemajú vracať žiadnu hodnotu majú špeciálny návratový typ void – ide typicky o funkcie, ktoré len vykonajú určitú činnosť, napríklad vypísanie textu na konzolu a podobne.
  • Identifikátor funkcie. Funkciu môžeme (v rámci určitých medzí) pomenovať prakticky ľubovoľne, rovnako ako pri premenných. Vhodné je však použiť názov, ktorý vystihuje úlohu danej funkcie (napríklad dlzka).
  • Zoznam vstupných parametrov funkcie. V zátvorkách za názvom funkcie je zoznam typov a identifikátorov vstupných argumentov, ktoré funkcia očakáva. V úvodnom príklade funkcia očakáva súradnice dvoch bodov v rovine, teda štyri hodnoty typu double. Ak funkcia neočakáva žiaden vstupný argument, môžeme do zátvoriek napísať void alebo nechať zoznam argumentov prázdny.
  • Telo funkcie. Do zložených zátvoriek za definíciou funkcie píšeme postupnosť príkazov, ktoré má funkcia vykonať.
  • Príkaz return. Vracia návratovú hodnotu funkcie.
typ_navratovej_hodnoty identifikator_funkcie(zoznam_vstupnych_argumentov) {
    telo_funkcie // Môže obsahovať príkazy return.
}

Ďalšie príklady funkcií

Súčet čísel od a po b

Ukážeme si tri verzie funkcie, ktorá dostane dvojicu celých čísel a, b a spočíta súčet všetkých celých čísel od a po b vrátane.

  • Prvá verzia tento súčet vráti ako návratovú hodnotu.
  • Druhá verzia súčet vypíše, vrátane sčitovaných čísel, jej návratový typ bude void.
  • Tretia verzia súčet aj vypíše aj vráti.
/* Funkcia, ktora vrati sucet a + (a+1) + ... + (b-1) + b */
int sum(int a, int b) {
    int result = 0;
    for (int i = a; i <= b; i++) {
        result += i;
    }    
    return result;
}

/* Funkcia, ktora vypise cisla a, (a+1), ..., b  a ich sucet */
void printNumbers(int a, int b) {
    int result = 0;
    for (int i = a; i <= b; i++) {
        if (i > a) {
            cout << " + ";
        }
        cout << i;
        result += i;
    }    
    cout << " = " << result << endl;
}

/* Funkcia, ktora vypise cisla a, (a+1), ..., b  a ich sucet
 * a tento sucet aj vrati. */
int sumAndPrint(int a, int b) {
    int result = 0;
    for (int i = a; i <= b; i++) {
        if (i > a) {
            cout << " + ";
        }
        cout << i;
        result += i;
    }    
    cout << " = " << result << endl;
    return result;
}

Program využívajúci tieto funkcie môže vyzerať napríklad takto:

#include <iostream>
using namespace std;

// sem pridu definicie funkcii sum, printNUmbers, sumAndPrint 
// uvedene vyssie

int main() {
    int a, b;
    
    cout << "Zadaj dvojicu celych cisel: ";
    cin >> a >> b;

    cout << "Test funkcie sum" << endl;
    cout << "Sucet celych cisel od " << a << " po " << b << ": ";
    // zavolame funkciu a vysledok priamo vypiseme
    cout << sum(a, b) << endl;
    // vysledok funkcie mozeme ulozit do premennej 
    // a pouzit neskor:
    int sucet = sum(a, b); 
    cout << "Druha mocnina suctu cisel je: " << sucet * sucet << endl;

    cout << endl << "Test funkcie printNumbers" << endl;
    // tato funkcia nema vysledok, nic neukladame
    printNumbers(a, b);
    
    cout << endl << "Test funkcie sumAndPrint" << endl;
    // dalsia funkcia vypise aj vrati hodnotu
    int sucet2 = sumAndPrint(a, b);
    cout << "Druha mocnina suctu cisel je: " << sucet2 * sucet2 << endl;
    
    // vystupnu hodnotu funkcie mozeme aj odignorovat
    sumAndPrint(a,b); 
}

Príkaz return

Pozrime sa teraz bližšie na špeciálny príkaz return:

  • Po vykonaní príkazu return je funkcia okamžite zastavená a jej výstupná hodnota je výraz za slovom return.
  • Funkcia môže obsahovať aj viacero volaní return. Akonáhle sa však jedno z nich vykoná, funkcia končí s danou návratovou hodnotou. Napríklad:
#include <iostream>
using namespace std;

int f(void) {
    return 1;
    return 2; // Nikdy sa nevykona.
    return 3; // Nikdy sa nevykona.
}

int main() {
    cout << f() << endl; // Vypise 1.
}
  • Funkcia s návratovým typom void môže tiež obsahovať príkaz return, avšak bez návratovej hodnoty.
    • V takom prípade return slúži iba na ukončenie vykonávania funkcie, väčšinou je lepšie prepísať inak
    • Tu je ukážka, kde namiesto return by bolo lepšie použiť else:
#include <iostream>
using namespace std;

void akeCislo(int n) {
    if (n >= 0) {
        cout << "Cislo je nezaporne." << endl;
        return;
    }

    // Vykona sa len v pripade n < 0:
    cout << "Cislo je zaporne." << endl; 
}

int main() {
    int n;
    cin >> n;
    akeCislo(n);
}
  • Napríklad minimum z dvoch čísel môžeme vypočítať dvoma rôznymi spôsobmi:
int minimum1(int a, int b) {
    int minval;
    if (a < b) {
        minval = a;
    } else {
        minval = b;
    }
    return minval;
}

int minimum2(int a, int b) {
    if (a < b) {
        return a;
    } else {
        return b;
    }
}
  • Funkcie s návratovým typom iným ako void je žiadúce písať tak, aby na ľubovoľnom vstupe ich vykonávanie vždy skončilo príkazom return.
    • Ak totiž takáto funkcia skončí inak, než príkazom return, jej výstupná hodnota je nedefinovaná (použije sa „hocijaký nezmysel”), čo môže viesť k zákerným chybám.

Cvičenie: Nájdite hodnotu nasledujúcej funkcie pre n=6 a n=7. Viete stručne popísať, čo funkcia robí pre všeobecné n?

int zahada(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return i; 
        }
    }
    return n;
}

Lokálne a globálne premenné

Premenné v C/C++ možno rozdeliť na globálne a lokálne:

  • Globálne premenné možno používať vo všetkých funkciách, ktoré sú v programe definované za touto premennou.
  • Lokálne premenné sú definované vo vnútri funkcie a môžu sa používať iba v rámci nej (alebo iba v rámci niektorej časti funkcie, ak je premenná definovaná napríklad v tele cyklu a podobne).

Platí navyše, že:

  • Viaceré funkcie môžu mať lokálne premenné s tým istým názvom – každá funkcia potom používa tú svoju. Toto sa bežne používa.
  • Ak má lokálna premenná rovnaký názov ako nejaká globálna premenná, lokálna premenná prekryje globálnu – funkcia teda používa svoju lokálnu premennú (bližšia košeľa ako kabát). Toto radšej nerobte, môže vzniknúť chaos.

Je silno odporúčané používať predovšetkým lokálne premenné. Väčšie programy s globálnymi premennými môžu byť veľmi neprehľadné.

Nasledujúci program obsahuje globálnu premennú x a v každej funkcii lokálnu premennú s názvom y:

#include <iostream>
using namespace std;

int x;

void f(void) {
    int y = 10;
    cout << x << " " << y << endl;      
}

int main() {
    x = 10;
    int y = 20;
        
    f();                            // Vypise 10 10.  
    cout << x << " " << y << endl;  // Vypise 10 20.
}

Parametre funkcií

Odovzdávanie parametrov hodnotou

Vstupné parametre funkcií sa správajú ako lokálne premenné danej funkcie. Pri volaní funkcie sa každému parametru priradí určitá hodnota. Uvažujme napríklad funkciu

void f(int a, int b) {
    // ...
}

Pri volaní

f(1,2);

sa parametru a priradí hodnota 1 a parametru b sa priradí hodnota 2. Tieto sa ďalej správajú ako lokálne premenné funkcie f. Možno ich teda meniť, ale táto zmena sa neprejaví na mieste, odkiaľ funkciu voláme. Tento mechanizmus nazývame odovzdávaním parametrov hodnotou.

Príklad:

#include <iostream>
using namespace std;

void f(int n) {
    n++;
    cout << n << endl;
}

int main() {
    int n = 1;
    f(n);               // Vypise 2
    cout << n << endl;  // Vypise 1
}

Odovzdávanie parametrov referenciou

V prípade, že pred názov niektorého parametra v hlavičke funkcie napíšeme &, parameter sa bude odovzdávať referenciou.

  • Za takýto parameter možno pri volaní funkcie dosadiť iba premennú (kým pri odovzdávaní hodnotou môžeme použiť napríklad aj konštanty alebo iný výraz).
  • Namiesto hodnoty sa funkcii pošle adresa premennej v pamäti (referencia).
  • Funkcia potom bude túto premennú používať pod novým názvom; jej prípadné zmeny sa prejavia aj na mieste, odkiaľ bola funkcia volaná.

Príklad:

#include <iostream>
using namespace std;

void f(int &n) {
    n++;
    cout << n << endl;
}

int main() {
    int n = 1;
    f(n);               // Vypise 2.
    cout << n << endl;  // Vypise 2.
}

Ďalej si ukážeme niekoľko použití odovzdávania parametrov referenciou.

Viac ako jedna návratová hodnota

Odovzdávanie parametra referenciou používame napríklad vtedy, keď potrebujeme vrátiť viac ako jednu výstupnú hodnotu.

Napríklad nasledujúca funkcia stred dostane súradnice dvoch bodov [x1,y1] a [x2,y2] a do parametrov [xm,ym], ktoré sú odovzdané referenciou, uloží súradnice stredu úsečky spájajúcej body [x1,y1] a [x2,y2].

#include <iostream>
using namespace std;

void stred(double x1, double y1, double x2, double y2, 
           double &xm, double &ym) {
    xm = (x1 + x2) / 2;
    ym = (y1 + y2) / 2;
}

int main() {
    double Ax, Ay, Bx, By;
    
    cout << "Zadaj suradnice bodu A: ";
    cin >> Ax >> Ay;
    cout << "Zadaj suradnice bodu B: ";
    cin >> Bx >> By;
    
    double Mx, My;
    stred(Ax, Ay, Bx, By, Mx, My);
    cout << "Stred usecky AB je [" 
         << Mx << ", " << My << "]." << endl; 
}

Funkcia swap

Typickým príkladom použitia odovzdávania parametrov referenciou je funkcia swap, ktorá vymení hodnoty dvoch premenných, ktoré dostane ako parametre.

#include <iostream>
using namespace std;

void swap(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}

int main() {
    int x, y;
    cin >> x >> y;
    
    swap(x, y);
    
    cout << "x = " << x << ", y = " << y << endl; 
}

Keby sme funkcii odovzdali parameter hodnotou – čiže by sme funkciu swap definovali s hlavičkou void swap(int a, int b); – vo funkcii main by sa premenné nevymenili.

Ošetrovanie chybných vstupov

Občas môže nastať potreba, aby parametre funkcie spĺňali určité podmienky. V takom prípade môže byť potrebné korektne sa vysporiadať aj so vstupmi, ktoré tieto podmienky nespĺňajú. Ukážeme si teraz zopár možných prístupov k tomuto problému.

Funkcia neošetrujúca chybné vstupy

Uvažujme napríklad nasledujúcu funkciu, ktorá počíta súčet všetkých deliteľov čísla n. Jej základným predpokladom je, že n je kladné celé číslo.

#include <iostream>
using namespace std;

int sumOfDivisors(int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        if (n % i == 0) {
            sum += i;    
        }        
    }   
    return sum;  
}

int main() {
    int n;
    cout << "Zadaj kladne cele cislo: ";
    cin >> n;
        
    cout << "Sucet delitelov " << n << ": " 
         << sumOfDivisors(n) << "." << endl;
}

Ak však používateľ zadá na vstupe nejaké záporné číslo, funkcia vždy vráti nulu, čo nie je úplne v súlade s očakávaním. Jednou možnosťou by samozrejme bolo prerobiť funkciu tak, aby pracovala správne aj na záporných vstupoch. Sú však aj situácie, keď podobné riešenie nie je možné. Ukážeme si preto ďalšie spôsoby, ako sa s nekorektným vstupom vysporiadať.


Použitie funkcie assert

V prípade použitia nekorektného vstupu napríklad môžeme celý program ihneď ukončiť. Pohodlný spôsob, ako to spraviť, je použitie funkcie assert (treba #include <cassert>). Táto funkcia kontroluje platnosť nejakej podmienky. Ak je táto podmienka splnená, program normálne pokračuje; v opačnom prípade sa program zastaví s chybovou hláškou. Argumentom funkcie assert môže byť ľubovoľná booleovská hodnota.

#include <iostream>
#include <cassert>
using namespace std;

int sumOfDivisors(int n) {
    assert(n > 0);
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        if (n % i == 0) {
            sum += i;    
        }        
    }   
    return sum;  
}

int main() {
    int n;
    cout << "Zadaj kladne cele cislo: ";
    cin >> n;
        
    cout << "Sucet delitelov " << n << ": " 
         << sumOfDivisors(n) << "." << endl;
}

Úspech výpočtu ako výstupná hodnota

Často je ale neprípustné v prípade jediného volania funkcie s nekorektnými vstupmi ukončiť celý program. Chceli by sme teda vrátiť dve hodnoty: samotný súčet deliteľov a indikátor, či boli argumenty zadané správne.

  • Napríklad súčet deliteľov môžeme ukladať do parametra odovzdávaného referenciou.
  • Výstupom funkcie bude booleovská hodnota, ktorá bude true práve vtedy, keď výpočet funkcie prebehol správne (t.j. keď boli zadané správne argumenty). Túto výstupnú hodnotu je potom možné použiť pri volaní funkcie ako známku toho, že hodnota v parametre predanom referenciou je zmysluplná.
#include <iostream>
using namespace std;

bool sumOfDivisors(int n, int &sum) {
    if (n <= 0) {
        return false;
    } else {
        sum = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 0) {
                sum += i;    
            }        
        }   
        return true;
    }  
}

int main() {
    int n, sum;
    cout << "Zadaj kladne cele cislo: ";
    cin >> n;
    
    if (sumOfDivisors(n, sum)) { 
        cout << "Sucet delitelov " << n << ": " 
             << sum << "." << endl;
    } else {
        cout << "Zly vstup" << endl;
    }
}

Programy s viacerými funkciami

V programe možno volať iba funkcie, ktoré už predtým boli niekde definované.

Tento program typicky neskompiluje (vo vnútri f1 ešte nepozná f2)

void f1(void) {
    f2();
}

void f2(void) {
    cout << "Hello, World!" << endl;
}

Tento program je v poriadku:

void f2(void) {
    cout << "Hello, World!" << endl;
}

void f1(void) {
    f2();
}

Funkcia main

Špeciálnou funkciou je v C/C++ funkcia main.

  • V programe ju nemožno volať – jediné jej volanie je automatické (začína sa ním beh programu).
  • Výstupná hodnota funkcie main sa interpretuje ako „spôsob ukončenia” programu (napríklad 0 pre korektné ukončenie, iné hodnoty pre chyby).
    • Funkciu main môžeme teda ukončit napríklad príkazom return 0.
  • Funkcia main môže mať aj určité presne špecifikované parametre (viac o tom neskôr).

Funkcie: zhrnutie

  • Funkcie nám umožňujú rozbiť väčší program na menšie logické časti a tým ho sprehľadniť. Tiež nám umožňujú vyhnúť sa opakovaniu podobných kusov kódu.
  • Hlavička funkcie obsahuje návratový typ (môže byť void), meno funkcie, typy a mená parametrov.
  • V tele funkcie sú samotné príkazy. Vypočítanú hodnotu vrátime príkazom return.
  • Lokálne premenné sú viditeľné len vo funkcii, ktorá ich definuje, globálne vo všetkých funkciách.
  • Parametre odovzdávané hodnotou sú lokálne premenné inicializované hodnotami, ktoré sa zadajú pri volaní funkcie.
  • Parametre odovzdávané referenciou (&) sú len novým menom pre inú premennú.