přejít na obsah přejít na navigaci

Linux E X P R E S, Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie

EDU Trainings IT školení

Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie

cplusplus.png

Funkcie môžu vrátiť premenné typu int, float či string. Môžu však funkcie vrátiť pointer, čiže adresu na nejakú premennú? Odpoveď na túto otázku sa dozviete v článku.


Pointer ako návratová hodnota funkcie

V minulých dieloch sme sa naučili, že funkcie môžu vraciať návratovú hodnotu, ktorá je nejakého typu, napr. int, float, double či string. Môže ale vrátiť pointer? To si ukážeme v sérii nasledujúcich príkladov.

Najprv si dajme príklad, ktorý by mohol byť potencionálne riešený cez pointer ako návratová hodnota funkcie. Nebojte sa, referenčným príkladom bude dobré známy faktoriál.

#include <iostream>
using namespace std;

int vypocetFaktorialu(int volba)
{
    int faktorial = 1;

    for(int i = volba; i > 1; i--)
    {
        faktorial = i * faktorial;
    }
    return faktorial;
}

int main()
{
    int volba;

    cout << "Zadajte cislo od 0 do 10 z ktoreho chcete vypocitat faktorial: ";
    cin >> volba;

    if(volba == 0 || volba == 1)
    {
        cout << "Faktorial cisla "<<volba<<" je: 1";
        return 0;
    }

    if(volba < 0 || volba > 10)
    {
        cout << "Nepripustna operacia";
        return 0;
    }

    cout << "Faktorial cisla "<<volba<< " je: "<<vypocetFaktorialu(volba)<<"\n";

    return 0;
 }

Výsledok:

Zadajte cislo od 0 do 10 z ktoreho chcete vypocitat faktorial: 7
Faktorial cisla 7 je: 5040

Teraz skúsme funkciu int vypocetFaktorialu(int volba) prepracovať tak, aby vrátila nie hodnotu typu int, ale adresu na premennú typu int, čiže pointer.

#include <iostream>
using namespace std;

int *vypocetFaktorialu(int volba)
{
    int faktorial = 1;

    for(int i = volba; i > 1; i--)
    {
        faktorial = i * faktorial;
    }

    int *vysledok=new int;    
    *vysledok=faktorial;    
    return vysledok;
}

int main()
{
    int volba;

    cout << "Zadajte cislo od 0 do 10 z ktoreho chcete vypocitat faktorial: ";
    cin >> volba;

    if(volba == 0 || volba == 1)
    {
        cout << "Faktorial cisla "<<volba<<" je: 1"<<"\n";
        return 0;
    }

    if(volba < 0 || volba > 10)
    {
        cout << "Nepripustna operacia\n";
        return 0;
    }

    cout << "Faktorial cisla "<<volba<< " je: "<<*vypocetFaktorialu(volba)<<"\n";
    return 0;
 }

Čo sa zmenilo? Pridali sme symbol * medzi int a názvom funkcie, viď. int *vypocetFaktorialu(int volba). Nie je striktné obmedzené, kde presne dáte *, ale musí byť medzi typom návratovej hodnoty a menom funkcie. Takže kľudne môžete napísať i takto: int* vypocetFaktorialu(int volba). Takto definovaná funkcia vracia adresu na premennú typu int. Preto pri return funkcie musí byť pointer a nie nejaká obyčajná premenná!

Posledná zmena je vo funkcii main, kde voláme našu funkciu takto: *vypocetFaktorialu(volba). Keďže máme záujem vypísať priamu hodnotu pointeru, napíšeme k našej funkcii dereferenčný operátor *.

Jednou z výhod adresy ako návratovej hodnoty funkcie môže byť, že celú problematiku pointerov riešite vo funkciách a v hlavnej funkcii main už len priamo pristupujete k návratovým hodnotám funkcií. Takto môžete mať kód čitateľnejší a všetky dôležité veci riešite vo funkciách.

Kebyže len predsa chcete vytvárať pointery v hlavnej funkcii main(), tak môžete použiť aj adresu ako návratovú hodnotu funkcie v deklarácii nového pointeru.

#include <iostream>
using namespace std;

int* vypocetFaktorialu(int volba)
{
    int faktorial = 1;

    for(int i = volba; i > 1; i--)
    {
        faktorial = i * faktorial;
    }

    int *vysledok=new int;
    *vysledok=faktorial;
    return vysledok;
}

int main()
{
    int volba;

    cout << "Zadajte cislo od 0 do 10 z ktoreho chcete vypocitat faktorial: ";
    cin >> volba;

    if(volba == 0 || volba == 1)
    {
        cout << "Faktorial cisla "<<volba<<" je: 1"<<"\n";
        return 0;
    }

    if(volba < 0 || volba > 10)
    {
        cout << "Nepripustna operacia\n";
        return 0;
    }

    int *faktorial=vypocetFaktorialu(volba);

    cout << "Faktorial cisla "<<volba<< " je: "<<*faktorial<<"\n";
    return 0;
 }

Námet na tento článok poslúžili tieto zdroje:
Return Pointer from Functions in C++, tutorialspoint.com, dostupné online
C++ Examples: Returning a Pointer, functionx.com, dostupné online

Nahoru

Příspěvky

Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Radek Chalupa 9. 11. 2019, 08:20:26
Odpovědět  Odkaz 
Obávám se že je to cesta do pekel. Vracíš adresu lokální proměnné, která je po opuštění funkce neplatná. To že tam zůstane "správná" hodnota bezprostředně po zavolání funkce na věc nic nemění. Pokud si výslednou adesu uložíš a vypíšeš po několika dalších voláních, zjistíš že se hodnota na té adrese změnila. Viz příklad:

int* funkce()
{
int cislo = rand() % 10 + 1;
int* pres = &cislo;
return pres;
}

void test()
{
srand(time(nullptr));
int* vysl = funkce();
printf("%d\n", *vysl);
for (size_t i = 0; i < 5; i++)
{
printf("%d ", *funkce());
}
printf("\n%d\n", *vysl);
}

a výstup je:
7
2 8 10 7 9
9

Takže z původní 7 je tam 9, tj. výsledek posledního volání.
Tomáš Crhonek Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Tomáš Crhonek 9. 11. 2019, 11:45:36
Odpovědět  Odkaz 
Přesně tak. Navíc v odkazovaném článku deklarují tu vnitřní proměnnou jako static, což sice jednak umožňuje na ní vracet pointer, ale současně je potřeba dát si pozor na to, že tato proměnná je sdílená přes všechny běhy dané funkce.

Tj pokud by funkce měla v každém volání generovat jiná data, tak není možné použít ani static proměnnou a je nutné pokaždé vytvořit (new) nový objekt v paměti a na ten dát odkaz (a potom řešit jejich rušení).

https://www.tutorialspoint.com/cplusplus/cpp_return_pointer_from_functions.htm
Eduard Boldižár Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Eduard Boldižár 9. 11. 2019, 12:55:19
Odpovědět  Odkaz 
Ja viem čo chceš povedať a áno súhlasím, že lokálna premenná zmizne a keby som ju chcel zavolať v main funkcii, tak dostanem od kompilátora svoje. Ale mi odkazujeme na funkciu a jej návratovú hodnotu a nie na jej lokálnu premennú! Takže logicky po každom volaní funkcie sa adresa funkcie s jej návratovou funkciou zmení. Treba si uvedomiť, že aj funkcia má svoju adresu a práve s ňou celý čas pracujeme.

S adresami funkcii pracujeme napr.tu: https://www.linuxexpres.cz/software/programovanie-v-jazyku-c-pointery-ukazujuce-na-funkcie ;)
Eduard Boldižár Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Eduard Boldižár 9. 11. 2019, 13:07:38
Odpovědět  Odkaz 
Ok možno k tomu ešte niečo vysvetlím. Ak mi dáme ako return pointer1;, tak funkcia si do návratovej hodnoty skopiruje takto pointer2=pointer1 a nie štýlom pointer2=*pointer1, ako by sa mohlo zdať. Nový skopírovaný pointer je zviazaný s adresou funkcie a preto môžete ho odkazovať kdekoľvek, aj keď len raz :) pretože pri ďalšom volaní sa vykoná zasa kód a vznikne nový pointer a tak dookola :)
Eduard Boldižár Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Eduard Boldižár 9. 11. 2019, 13:23:16
Odpovědět  Odkaz 
I keď to som asi tiež domotal. :) Možno funkcia skutočne uchováva takto: premennaInt=*pointer1. Avšak vypísal som si adresy tej funkcie i jej hodnoty a pravdepodobne i s deštrukciou lokálnej premennej stále uchovava danú hodnotu, čiže asi kvôli uloženej hodnoty z pointeru do klasíckého int a potom je to už len o adrese funkcie a jej hodnote. Snáď je to tak ako som povedal, ale môžem sa i mýliť :)
Tomáš Crhonek Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Tomáš Crhonek 9. 11. 2019, 13:24:41
Odpovědět  Odkaz 
Ta funkce ale nevytváří nový paměťový prostor pro uložení té proměnné. To byste tam musel mít něco jako:


int * pint = new int;
*pint = result;
return pint;


tj vytvořit si místo pro int, získat pointer na toto místo (vrací operátor new) a na toto místo uložit výsledek vašeho výpočtu a tento pointer vrátit.

Jinak vám to bude fungovat jen náhodou, protože platnost lokálních proměnných končí po ukončení bloku a kompilátor tam může umístit jiná data.
Eduard Boldižár Re: Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Eduard Boldižár 9. 11. 2019, 13:30:11
Odpovědět  Odkaz 
Ach tak, už chápem :) vďaka, aspoň je to namet na ďalší článok.
Tomáš Crhonek Re: Re: Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Tomáš Crhonek 9. 11. 2019, 13:37:25
Odpovědět  Odkaz 
Není zač. Potom je ovšem nutné řešit rušení těchto objektů, protože takto může snadno dojít k memory leakům. Proto také existují smart pointers apod, které tuto problematiku zjednodušují.
Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
pepa_u 14. 11. 2019, 11:42:17
Odpovědět  Odkaz 
Asi by to chtelo bud v tom clanku opravit a nebo ten clanek smazat, protoze jestli se tim bude nekdo insporovat, prida si starosti....
Eduard Boldižár Re: Programovanie v jazyku C++: Pointer ako návratová hodnota funkcie
Eduard Boldižár 14. 11. 2019, 13:11:28
Odpovědět  Odkaz 
Aj keď mám v pláne túto problematiku ešte rozviesť v ďalšom článku, po vašom komentári som sa zamyslel a asi by nebolo fér pre tých, ktorí si prečítajú len tento článok a ten budúci z akéhokoľvek dôvodu nie. Takže opravil som problém s pamäťou.

Odpovědět

Nejsou podporovány žádné značky, komentáře jsou jen čistě textové. Více o diskuzích a pravidlech najdete v nápovědě.
Diskuzi můžete sledovat pomocí RSS kanálu rss



 
 

Top články z OpenOffice.cz

Eduard Boldižár

Eduard Boldižár

Som redaktorom stránky astrotech.cz. Mám 25 rokov. Medzi moje záľuby patrí astronómia, sci-fi literatúra a programovanie.


  • Distribuce: ubuntu
  • Grafické prostředí: unity


EDU Trainings

Redakční blog

Pavel Fric

Pavel Fric, 21. srpen

Sayonara Player 1.5.1

Přehrávač, jak má být. Poslední dobou vývoj šlape


Pavel Fric

Pavel Fric, 26. leden

MuseScore 3

První aktualizace třetí řady notačního editoru MuseScore


Redakce

Redakce, 21. prosinec

Pište pro LinuxEXPRES

Baví vás Linux? Pište o něm, není to nic těžkého. LinuxEXPRES hledá nové autory.


Všechny blogy »

Lokia