Tipy a triky pri pointerovej aritmetike
Ukážme si príklady, kedy pointerová aritmetika môže zmiasť a programátor nedopatrením urobí chybu, ktorá sa mu môže vypomstiť. Uvidíme tri rôzne možností, ako môžete využiť nakombinovať pointerovú aritmetiku.
V prvom príklade využijeme tento tvár: *ptr++.
#include <iostream> using namespace std; int main() { int pole[] = {55,22,78,91,27,187,155}; int *ptr = pole; for(int i = 0; i < 3; i++) { cout << *ptr++ << " "; } cout << "\nUkazovatel ukazuje: " << *ptr; return 0; }
Výstup programu:
55 22 78 Ukazovatel ukazuje: 91
Použitím *ptr++ dôjde najprv k inkrementácii adresy a potom a teraz pozor, dereferencia sa týka predošlej adresy, nie aktuálnej po inkrementácii!. Prečo to funguje takto podivné? Pretože postfix ++ má prednosť pred prefixom, ktorý predstavuje znak dereferencie *. Avšak pri tomto definovaní sa tiež môžete ľahko dostať do problému s pamäťou. Ak by ste chceli vypísať celé pole, tak pri poslednej iterácii dôjde k tomu, že pointer bude ukazovať mimo vašu vyhradenú pamäť a môže dôjsť v tom lepšom prípade len k pádu programu. Ako sa brániť proti tomuto druhu potencionálnej chyby? Napr. pridaním podmienky if/else.
#include <iostream> using namespace std; int main() { int pole[] = {55,22,78,91,27,187,155}; int *ptr = pole; for(int i = 0; i < 7; i++) { if(i != 6) cout << *ptr++ << " "; else cout << *ptr << " "; } cout << "\nUkazovatel ukazuje: " << *ptr; return 0; }
Výstup programu:
55 22 78 91 27 187 155 Ukazovatel ukazuje: 155
Druha možnosť využitia pointerovej aritmetiky je v tváre *++p.
#include <iostream> using namespace std; int main() { int pole[] = {55,22,78,91,27,187,155}; int *ptr = pole; for(int i = 0; i < 3; i++) { cout << *++ptr << " "; } cout << "\nUkazovatel ukazuje: " << *ptr; return 0; }
Výstup programu:
22 78 91 Ukazovatel ukazuje: 91
Ak sú oba operátory prefixové, čiže majú rovnakú prioritu a vtedy sa vykonávajú operátory sprava do ľavá. Najprv sa inkrementuje adresa pointera a následne sa aplikuje dereferenčný operátor.
Problémy s pamäťou sa iste objavia, ak si neustriehnete pridelený blok pamäte. Skúsme sa pozrieť na alternatívne riešenie odlišné od prvého riešenia tak, aby sme skúsili viacero možností v tomto dieli.
#include <iostream> using namespace std; int main() { int pole[] = {55,22,78,91,27,187,155}; int *ptr = pole; for(int i = 0; i < 7-1; i++) { cout << *++ptr << " "; } cout << "\nUkazovatel ukazuje: " << *ptr; return 0; }
Výsledok programu:
22 78 91 27 187 155 Ukazovatel ukazuje: 155
Vyriešili sme to tak, že sme počet iterácii obmedzili podmienkou i < 7-1, kde 7 je počet prvkov v polí a od toho odčítame práve tu poslednú problematickú iteráciu. Ako z pointera zistiť veľkosť poľa sa dozviete v jednom z ďalších dieloch. Zatiaľ sme to definovali takto provizórne.
A teraz sa pozrime na poslednú tretiu možnosť pointerovej aritmetiky.
#include <iostream> using namespace std; int main() { int pole[] = {55,22,78,91,27,187,155}; int *ptr = pole; for(int i = 0; i < 3; i++) { cout << ++*ptr << " "; } cout << "\nUkazovatel ukazuje: " << *ptr; return 0; }
Výsledok programu:
56 57 58 Ukazovatel ukazuje: 58
Pri tretej možností kombinácie pointerovej aritmetiky s dereferenčným operátorom ++*ptr, opäť nastáva to, že oba operátory sú prefixy, čiže majú rovnakú prioritu. Vykonaním sprava do ľavá najprv sa aplikuje dereferenčný operátor a následne sa inkrementuje hodnota prvku, ktorú sme získali dereferenčným operátorom a nie adresa! Nikdy nedôjde k zmene adresy! Taktiež nehrozí žiadny problém s pamäťou, pretože stále bude pointer ukazovať na ten istý prvok. Presvedčme sa.
#include <iostream> using namespace std; int main() { int pole[] = {55,22,78,91,27,187,155}; int *ptr = pole; for(int i = 0; i < 25; i++) { cout << ++*ptr << " "; } cout << "\nUkazovatel ukazuje: " << *ptr; return 0; }
Výsledok programu:
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 Ukazovatel ukazuje: 80
Námet na tento článok poslúžili tieto zdroje:
Difference between ++*p, *p++ and *++p, geeksforgeeks.org, dostupné online
Can you explain the difference between *ptr++ and *(++ptr)? [duplicate], stackoverflow.com, dostupné online