1. Wskaźniki, tablice i listy dynamiczne

1.1. Wskaźnik

Każda komórka pamięci ma swój unikalny adres, który jest liczbą całkowitą (zwykle w systemie szesnastkowym, np. 0x7FFEEB1A2C). Adres jest używany przez procesor do lokalizowania i manipulowania danymi. 

  • Jednostka pamięci: Najczęściej jest to jeden bajt, który jest najmniejszym adresowalnym blokiem pamięci.
  • Adres bazowy: Pierwszy bajt każdej zmiennej (lub bloku pamięci) ma swój adres bazowy.
  • Słowo pamięci: W zależności od architektury (np. 32-bit, 64-bit), procesor może przetwarzać dane w jednostkach większych niż bajt.

 Do danych w pamięci możemy odwoływać się nie tylko przez zwykłe nazwy, ale również przez adresy (wskaźniki) do nich. 

Wskaźnik wskazujący jakąś daną w pamięci ilustruje się zwykle następująco:


Zmienna wsk wskazuje na miejsce w pamięci gdzie znajduje się zadeklarowana zmienna. Nazwy wskaźników podobnie jak nazwy zmiennych mogą być dowolne (bez znaków specjalnych). 

Deklaracja wskaźnika ma postać:
typ_zmiennej wskazywanej *nazwa_wskaźnika;

Jeżeli chcemy utworzyć zmienną dynamiczna oraz zapamiętać adres do tej zmiennej w pamięci możemy to zrobić w następujący sposób:


int* wsk = new int;

Operacja new int tworzy zmienną dynamiczną w pamięci. Adres zmiennej jest zapisywany we wskaźniku wsk. Możliwe jest wyświetlenie adresu zmiennej. Po wykonaniu następujacego kodu:

cout << wsk;

Zostanie wyświetlony adres zmiennej na którą wskazuje zdefiniowany wskaźnik. Możemy także odwołać się do wartości wskazywanej przez wskaźnik za pomocą operatora wyłuskania (*).

*wsk = 20;
cout << *wsk; // Wyświetlona zostanie wartość 20

W celu usunięcia zmiennej dynamicznej z pamięci należy wykorzystać operator delete.

delete wsk;
Zostanie usunięta zmienna wskazywana przez danych wskaźnik (znajdująca się w pamięci pod danym adresem). Usunięcie zmiennej z pamięci nie powoduje "wyzerowania" wartości wskaźnika. Będzie on przechowywał adres zmiennej, która była w tym mijescu pamięci. Próba odwołania się do zmiennej usuniętej z pamieci sposoduje błąd (np. ang. Segmenattion Fault).

int* wsk = new int;
*wsk = 10;
delete wsk;
cout << *wsk; // Błąd

Aby programista mógł zaznaczyć, ze adres nie wskazuje w konkretne miejsce pamięci możliwe jest przypisanie do niego wartości NULL.

int* wsk = NULL; // Wskaźnik pusty

Dzięki temu możliwe jest sprawdzenie czy wskaźnik wskazuje na zmienną, czy może jest pusty. Oczywiście zależy to od programisty, który musi ustawić wartość wskaźnika na NULL np. po usunięciu zmiennej wskazywanej.
Po wywołaniu operatora delete nie jest automatycznie zerowany wskaźnik. Wskazuje dalej w to samo pamięci.
Zmienne dynamiczne mogą być różnych typów, tak jak wszystkie inne zmienne.
Umieszczając kilka wskaźników w jednej deklaracji trzeba uważać, by każdy z nich osobno był opatrzony gwiazdką po lewej stronie:
int *wsk1, *wsk2, *wsk3;    // tak deklarujemy kilka wskaźników
int *wsk1, wsk2, wsk3;    // a tu wskaźnikiem jest tylko wsk1, zaś wsk2 i wsk3  to zwykłe zmienne typu int