Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

1. diel - Úvod do ukazovateľov v C ++

Vitajte u prvého dielu pokročilých sekcie seriálov o programovaní v jazyku C ++. V tejto sekcii sa naučíme pracovať s dynamicky alokovanú pamäťou v jazyku C ++ a dostaneme sa aj k práci so súbormi. Asi vás neprekvapí, že predpokladom ku zdolanie seriálu je znalosť základov C ++.

Adresy v pamäti

Keď sme sa prvýkrát zmieňovali o premenných, hovorili sme si, že premenná je "miesto v pamäti", kam si môžeme uložiť nejakú hodnotu. Tiež vieme, že premenné majú rôzne dátové typy (napr. Int) a tie zaberajú v pamäti rôzne miesta (napr. Int zaberá 32 bitov, teda 32 núl a jednotiek).

Pamäť počítača si môžeme predstaviť ako dlhú (takmer nekonečnú :) ) Rad núl a jednotiek. Niektoré časti pamäte sú obsadené inými aplikáciami a niektoré sú operačným systémom chápané ako voľné miesto. Aby sa dalo s pamäťou rozumne pracovať, je adresovaná, ako sú napr. Domy v ulici. Adresy sa väčšinou zapisujú v šestnástkovej sústave, ale stále sa jedná o obyčajná čísla. Adresy idú chronologicky za sebou a na každej adrese sa nachádza 1 bajt (teda 8 bitov, pretože adresovanie po drobných bitoch by bolo nepraktické).

Akonáhle v C ++ deklarujeme nejakú premennú v zdrojovom kóde a aplikáciu spustíme, C ++ si povie operačnému systému o toľko pamäti, koľko je pre túto premennú treba. Od systému získa adresu do pamäte, na ktorú môže hodnotu premennej uložiť (zjednodušene povedané).

Získanie adresy premenné

Jazyk C ++ nás od adries zatiaľ plne odsťiňoval, pamäť zarezervoval za nás as premennými sme pracovali jednoducho pomocou ich mien. Vytvorme si teraz jednoduchý program, ktorý založí premennú typu int a do nej uloží hodnotu 56. Adresu tejto premennej si získame pomocou tzv. Referenčného operátoru & (ampersand) a vypíšeme ju do konzoly.

int main()
{
    int a;
    a = 56;
    cout << "Proměnná a s hodnotou " << a << " je v paměti uložená na adrese " << &a << endl;
    cin.get();
    return 0;
}

výsledok:

Adresa premenné v pamäti - Pokročilé konštrukcia C ++

Vidíte, že na mojom počítači si systém vybral adresu 0x23aadc. Vy tam budete mať iné číslo. Situácia v pamäti počítača bude vyzerať takto:

Uloženie premenné v pamäti - Pokročilé konštrukcia C ++

(Dátový typ int má 32 bitov, preto teda zaberá 4 osmice bitov a 4 adresy. Udávame vždy adresu začiatku hodnoty.)

Ukazovatele (pointer)

Získať číslo adresy je síce pekné, ale ak by sme s pamäťou takto pracovali, bolo by to trochu nepraktické. Z toho dôvodu jazyk C ++ podporuje tzv. Ukazovatele (anglicky Pointer). Ukazovateľ je premenná, ktorej hodnotou je adresa niekam do pamäte. C ++ ukazovateľ však neberie ako obyčajné číslo, ale vie, že ho má používať ako adresu. Keď do ukazovateľa teda niečo uložíme alebo z neho naopak niečo vypisujeme, nevypisuje sa adresa (hodnota ukazovateľa), ale používa sa hodnota, na ktorú ukazovateľ ukazuje.

Vráťme sa opäť k nášmu programu. Tentoraz si okrem premenné a definujeme aj ukazovateľ na premennú a. Ten bude tiež typu int, ale pred jeho názvom bude tzv. Dereferenční operátor * (hviezdička).

int main()
{
    int a = 56;
    int *p_a = &a; // Uloží do p_a adresu proměnné a
    *p_a = 15; // Uloží hodnotu 15 na adresu v p_a
    cout << "Ukazatel p_a ma hodnotu " << p_a << " a ukazuje na hodnotu " << *p_a << endl;
    cout << "Hodnota ulozena v a je " << a << endl;
    cin.get();
    return 0;
}

Aplikácia si vytvorí premennú typu int a ďalej ukazovateľ na int. Ukazovatele tiež majú vždy svoj dátový typ podľa toho, na hodnotu akého typu ukazujú. Do premennej a sa uloží hodnota 56.

Do ukazovateľa p_a (zatiaľ bez hviezdičky) sa uloží adresa premenné a, ktorú získame pomocou referenčného operátora &. Teraz budeme chcieť tam, kam ukazuje pointer p_a, uložiť číslo 15. Použijeme dereferenční operátor (*) a tým neuložíme hodnotu do ukazovateľa, ale tam, kam ukazovateľ ukazuje.

Následne vypíšeme hodnotu ukazovateľa (čo je nejaká adresa v pamäti, obvykle vysoké číslo, tu ho vypisujeme v šestnástkovej sústave) a ďalej vypíšeme hodnotu, na ktorú ukazovateľ ukazuje. Kedykoľvek pracujeme s hodnotou ukazovateľa (nie adresou), používame operátor *.

výsledok:

Editácia premenné ukazovateľom - Pokročilé konštrukcia C ++

Odovzdávanie referencií

Vieme teda na premennú vytvoriť ukazovateľ. K čomu je to ale dobré? Do premennej sme predsa vedeli ukladať aj predtým. Jednou z výhod pointer je tzv. Odovzdávanie referencií. Vytvorme si funkciu, ktoré prídu v parametri 2 čísla a my budeme chcieť, aby ich hodnoty prehodila (tejto funkcii sa anglicky hovorí swap). Naivne by sme mohli napísať nasledujúci kód:

// Tento kód nefunguje
void prohod(int a, int b)
{
    int pomocna = a;
    a = b;
    b = pomocna;
}

int main()
{
    int cislo1 = 15;
    int cislo2 = 8;
    prohod(cislo1, cislo2);
    cout << "V cislo1 je číslo " << cislo1 << " a v cislo2 je číslo " << cislo2 << endl;
    cin.get();
    return 0;
}

výsledok:

Zlá implementácia funkcie swap - Pokročilé konštrukcia C ++

Prečo že aplikácia nefunguje? Pri volaní funkcie prehodí vo funkcii main () sa zoberú hodnoty premenných číslo1 a číslo2 a tie sa skopírujú do premenných a a b v definícii funkcie. Funkcia ďalej zmení tieto premenné a a b, avšak pôvodné premenné číslo1 a číslo2 zostanú nezmenené. Tomuto spôsobu, kedy sa hodnota premennej do parametra funkcie skopíruje, hovoríme odovzdávanie hodnodnou.

Pozn .: K prehodenie 2 čísel potrebujeme pomocnú premennú. Keby sme vo funkcii prehodí () napísali len a = b; b = a ;, bola by v oboch premenných hodnota b, pretože hodnota a sa medzitým zmenila.

Ľubovoľnú premennú môžeme odovzdať referencií a to tak, že funkciu upravíme aby prijímala v parametroch Pointer a pri volaní funkcie použijeme referenčné operátor &:

void prohod( int *a, int *b )
{
    int pomocna = *a;
    *a = *b;
    *b = pomocna;
}

int main()
{
    int cislo1 = 15;
    int cislo2 = 8;
    prohod( &cislo1, &cislo2 );
    cout << "V cislo1 je cislo " << cislo1 << " a v cislo2 je cislo " << cislo2 << endl;
    cin.get();
    return 0;
}

výsledok:

Odovzdávanie referencií v jazyku C ++ - Pokročilé konštrukcia C ++

Keďže funkciu teraz odovzdávame adresu, je schopná zmeniť pôvodné premennú.

Niektorí programátori v jazyku C ++ používajú často parametre funkcií v odovzdávaní hodnoty. To však nie je príliš prehľadné a ak nás netlačia výpočtovej čas a je to len trochu možné, mala by funkcie vracať len jednu hodnotu pomocou príkazu return.

Odovzdávanie poľa

Polia a Pointer majú v C ++ veľa spoločného. Preto keď odovzdáme poľa do parametra nejakej funkcie a polia v nej zmeníme, zmeny sa v pôvodnom poli prejaví. Pole je na rozdiel od ostatných typov vždy odovzdávané referencií bez toho aby sme sa o to museli snažiť.

void napln_pole(int pole[], int delka)
{
    int i;
    for (i = 0; i < delka; i++)
    {
        pole[i] = i + 1;
    }
}

int main() {
    int cisla[10];
    napln_pole(cisla, 10);
    cout << cisla[5] << endl; // Vypíše číslo 6
    cin.get();
    return 0;
}

Ako sme si povedali skôr, pole je vlastne spojité miesto v pamäti. Ale také miesto musíme vedieť nejako adresovať. Adresujeme ho práve pomocou ukazovateľa. Samotné meno premennej u pole nie je nič iné ako ukazovateľ. To znamená, že nám bez problémov prejde nasledujúce operácie priradenie.

int pole[10];
int* p_pole = pole;

Null

Všetkým pointerům ľubovoľného typu môžeme priradiť konštantu NULL. Tá udáva, že je pointer prázdny a že zrovna na nič neukazuje. Na väčšine platforiem sa NULL rovná hodnote 0 a tak sa v niektorých kódoch môžete stretnúť s priradením 0 miesto NULL. To sa všeobecne neodporúča kvôli kompatibilite medzi rôznymi platformami. Túto hodnotu budeme v budúcnosti hojne používať.

Čo si zapamätať: Pointer je premenná, v ktorej je uložená adresa do pamäti. Môžeme pracovať buď s touto adresou alebo s hodnotou na tejto adrese a to pomocou operátora *. Adresu ľubovoľnej premennej získame pomocou operátora &.

Hoci sme si Pointer pomerne slušne uviedli, ich pravým účelom je najmä dynamické alokovanie pamäte, na ktoré sa pozrieme hneď v budúcom dieli.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 74x (10.94 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C++

 

Všetky články v sekcii
Pokročilé konštrukcia C ++
Preskočiť článok
(neodporúčame)
Dynamická správa pamäte v C ++
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity