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:
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:
(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:
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:
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:
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é 77x (10.94 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C++