Techniki Programowania wskaźniki Łukasz Madej Katedra Informatyki Stosowanej i Modelowania Wykłady opracowane we współpracy z Danutą Szeligą, Łukaszem Sztangretem
Wskaźniki Dla typu T zapis T* oznacza wskaźnik do T, czyli zmienna typu T* może przechowywać adres (w pamięci) obiektu typu T. int a = 1; int *p = &a; cout << *p;
Wskaźniki Podstawową operacją na wskaźniku jest wyłuskanie, czyli odwołanie się do obiektu pokazywanego przez ten wskaźnik. Operacja ta nazywa się adresowaniem pośrednim (indirection). Operatorem adresowania pośredniego jest przedrostkowy jednoargumentowy operator (wyłuskania) *. Operatorem dualnym do operatora wyłuskania jest przedrostkowy jednoargumentowy operator pobrania adresu & (zwraca adres obiektu).
int* p = &a; int* p; int a; p = &a
Wskaźniki Z definicji wskaźnika wynika, że wskaźnik pokazuje na obiekt. Referencja nie jest obiektem nie można definiować wskaźnika do referencji. Typ wskaźnika precyzyjnie określa, na jakie obiekty można takim wskaźnikiem pokazywać. Generalnie, wskaźnikiem typu A* nie można pokazywać na obiekty typu B.
Wskaźniki Definicja wskaźnika tworzy jedynie obiekt wskaźnikowy wskaźnik nie pokazuje na konkretny obiekt: int* p; // p pokazuje na "nie-wiadomo-co" Aby bezpiecznie używać wskaźnika, należy go ustawić: p = &n; // teraz p pokazuje na n Najbezpieczniej ustawić wskaźnik od razu (w definicji): int* p = 0; // p pokazuje na adres 0x00000000 Żaden obiekt nie może być umieszczony w pamięci pod adresem 0. Zero pełni rolę literału wskaźnikowego, oznaczającego wskaźnik, który nie pokazuje na żaden obiekt.
Tablice a wskaźniki long *c[10]; - 10 elementowa tablica wskaźników do elementów typu long Tablica (lub jej początek) jest obiektem w pamięci, ma więc zatem swój własny adres. Nazwa tablicy tab typu T[] jest adres jej pierwszego elementu &tab[0] Tablica tab typu T[] może być niejawnie skonwertowana do typu T*; Rezultatem jest wskaźnik do pierwszego elementu tablicy tab. Jeżeli mamy wskaźnik T* p, to operatory: * oraz [] działają identycznie: *p == p[0] *(p + i) == p[i]
Tablice a wskaźniki Tablica obiektów typu int: int a[10]; // a typu: int [10] int* pn = &a[3]; // pn typu: int* *pn = 7; // a[3] = 7 int* pa = a; // pa typu: int* // nastąpiła konwersja: int[] int* pa[3] = 7; // a[3] = 7 Tablica obiektów typu int* (wskaźników): int n; // n typu: int int* ap[10]; // ap typu: int *[10] ap[3] = &n; // ustawiamy adres n *ap[3] = 7; // n = 7
Tablice a wskaźniki Nazwa tablicy jest jednocześnie stałym wskaźnikiem (adresem) do jej pierwszego elementu Możliwa jest niejawna konwersja z typu T[] na T* int t[] = {1, 4, -2}; // zmieniamy trzeci element tablicy *(t+2) = 5; // wynik: {1, 4, 5} int * p; // równoważnie można zapisać: p = &t[0]; // *p wynosi 1 p = t; // *p wynosi 1 // p wskazuje na ostatni element p = &t[2]; // *p wynosi 5
Tablice a wskaźniki Jeżeli wskaźnik pokazuje na element tablicy można użyć go zgodnie z notacją typową dla tablic: double tab[5] = {-2,-1,0,1,2}; double * ptr; ptr = &tab[0]; // lub: ptr = tab; for (int i = 0; i < 5; i++) cout << ptr[i] << ' ' << tab[i] << endl; -2,-1,0,1,2-2,-1,0,1,2
Tablice a wskaźniki Wskaźnik taki można przesuwać po elementach tablicy: double tab[5] = {-2,-1,0,1,2}; ptr++; for (int i = 0; i < 4; i++){ cout << *ptr << ' ' << tab[i] << endl; ptr++; } -1,0,1,2-2,-1,0,1
Tablice a wskaźniki Wskaźnik taki można przesuwać po elementach tablicy: ptr++; for (int i = 0; i < 4; i++){ cout << *ptr << ' ' << tab[i] << endl; ptr++; tab++; } Blad
Arytmetyka wskaźników Rezultat zastosowania do wskaźników operatorów arytmetycznych +, -, +=, -=, ++ i -- zależy od typu wskazywanego obiektu. Stosując operator arytmetyczny do wskaźnika p typu T* zakładamy, że p wskazuje na element w tablicy obiektów typu T. Wtedy: p + 1 oznacza adres następnego elementu w tablicy, p - 1 oznacza adres poprzedniego elementu w tablicy, p + i oznacza adres i-tego następnego elementu, p - i oznacza adres i-tego poprzedniego elementu
Arytmetyka wskaźników #include<iostream> using namespace std; int main() { int tab[]={0,1,2,3,8}; int *wsk1,*wsk2; wsk1=&tab[1]; wsk2=&tab[4]; cout << wsk2-wsk1 << endl; system("pause"); return 0; } 3
Arytmetyka wskaźników int main() { int tab[5]={1,2,3,4,5}; int *wsk; wsk=tab; for (int i=0;i<5;i++) { cout<<*wsk<<"\t"<<wsk<<endl; wsk++; } return 0; }
Porównywanie wskaźników Dwa wskaźniki tego samego typu są równe (różne) jeżeli pokazują (nie pokazują) na ten sam obiekt: int *wsk1,*wsk2; if (wsk1==wsk2){} if (wsk1!=wsk2){} //ten sam obiekt //różne obiekty Porównywanie wskaźników operatorami <, >, <= i >= jest zdefiniowane tylko wtedy, gdy oba wskaźniki wskazują na elementy tej samej tablicy. Wtedy, jeżeli w1 < w2, to oznacza to, że obiekt wskazywany przez w1 znajduje się w pamięci wcześniej niż drugi (w1 jest bliżej początku tablicy niż w2).
Wskaźniki i const zwykły wskaźnik: stały wskaźnik int *wsk int * const wsk wskaźnik do obiektu stałego const int * wsk stały wskaźnik do stałego obiektu const int * const wsk