Etap 10: Wskaźniki, operator adresu i dereferencji
Zrozumienie fundamentów adresowania pamięci w C++: jak działają wskaźniki i operatory niskopoziomowe.
Wskaźniki: Klucz do zrozumienia pamięci komputera
Wskaźniki (ang. pointers) to jeden z najpotężniejszych, a zarazem najbardziej wymagających elementów języka C++. W przeciwieństwie do zwykłych zmiennych, które przechowują konkretne wartości (jak liczby czy znaki), wskaźniki przechowują adresy w pamięci RAM. Zrozumienie ich działania jest niezbędne do pracy z dynamiczną alokacją pamięci, tablicami i zaawansowanymi strukturami danych.
1. Operator adresu (&) oraz deklaracja wskaźnika
Każda zmienna w programie zajmuje określone miejsce w pamięci. Aby dowiedzieć się, pod jakim adresem znajduje się zmienna, używamy operatora adresu &.
Deklaracja
Aby zadeklarować wskaźnik, używamy symbolu gwiazdki * przy typie danych. Typ wskaźnika musi odpowiadać typowi danych, na które wskazuje.
int liczba = 100;
int *wskaznik; // Deklaracja wskaźnika na liczbę całkowitą
wskaznik = &liczba; // Przypisanie adresu zmiennej 'liczba' do wskaźnika2. Operator dereferencji (*)
Kiedy mamy już wskaźnik przechowujący adres, chcemy zazwyczaj dostać się do wartości, która pod tym adresem się znajduje. Służy do tego operator dereferencji *.
Uwaga: Symbol * ma dwa znaczenia:
- W deklaracji:
int *ptr;oznacza "tworzę wskaźnik". - W kodzie:
*ptr = 20;oznacza "idź pod adres w ptr i wpisz tam 20".
#include <iostream>
int main() {
int x = 10;
int *p = &x;
std::cout << "Adres x: " << p << std::endl;
std::cout << "Wartość pod adresem p: " << *p << std::endl; // Wypisze 10
*p = 50; // Zmiana wartości x poprzez wskaźnik
std::cout << "Nowa wartość x: " << x << std::endl; // Wypisze 50
return 0;
}3. Wskaźnik pusty: nullptr
Wskaźnik, który nie został zainicjalizowany, zawiera przypadkowy adres (tzw. "śmieci"). Próba dereferencji takiego wskaźnika prawie zawsze kończy się błędem Segmentation Fault. Dlatego dobrą praktyką jest inicjalizacja wskaźnika wartością nullptr.
int *p = nullptr; // Wskaźnik na nic nie wskazuje
if (p != nullptr) {
std::cout << *p;
}4. Arytmetyka wskaźników i tablice
Tablice i wskaźniki są w C++ ściśle powiązane. Nazwa tablicy jest w rzeczywistości wskaźnikiem na jej pierwszy element.
tabto adres&tab[0]*(tab + 1)to to samo cotab[1]
Możemy przesuwać wskaźnik wzdłuż tablicy, dodając do niego liczby całkowite. C++ automatycznie przelicza to na bajty w zależności od typu danych (np. dla int przesunięcie o 1 to przesunięcie o 4 bajty).
int liczby[] = {10, 20, 30};
int *ptr = liczby; // Wskazuje na liczby[0]
std::cout << *ptr << " "; // 10
std::cout << *(ptr + 1) << " "; // 20Pułapki na kolokwium
- Niezainicjalizowane wskaźniki: Nigdy nie rób
*p = 5;jeśli wcześniej nie przypisałeś dopadresu istniejącej zmiennej lub pamięci dynamicznej. - Mylenie operatorów:
&- daj mi adres.*- wejdź pod ten adres.
- Wskaźniki a referencje: Referencja to tylko alias (druga nazwa), wskaźnik to osobna zmienna przechowująca adres. Wskaźnik może być zmieniony w trakcie programu, by wskazywał na coś innego – referencja nie.
Przykład: Funkcja z użyciem wskaźnika
Zamiast referencji, do zmiany wartości w funkcji możemy użyć wskaźnika:
void wyzeruj(int *p) {
if (p != nullptr) {
*p = 0;
}
}
int main() {
int a = 100;
wyzeruj(&a); // Przekazujemy adres zmiennej a
// a wynosi teraz 0
}Może Cię zainteresować
Etap 11: Arytmetyka wskaźników w C++
Zrozumienie, jak C++ operuje na adresach pamięci. Dowiedz się, dlaczego ptr++ to coś więcej niż dodanie jedynki.
Etap 3: Tablice automatyczne, jednowymiarowe w C++
Przewodnik po statycznych tablicach jednowymiarowych: deklaracja, inicjalizacja i bezpieczna iteracja.
Etap 6: Obsługa napisów w stylu C (char[])
Przewodnik po niskopoziomowym przetwarzaniu napisów jako tablic znaków zakończonych zerem, zgodnie z ograniczeniami kolokwium.