12.9 — Wskaźniki i const

Rozważ następujący fragment kodu:

int main()
{
    int x { 5 };
    int* ptr { &x }; // ptr jest normalnym (nie stałym) wskaźnikiem

    int y { 6 };
    ptr = &y; // możemy wskazać inną wartość

    *ptr = 7; // możemy zmienić wartość pod przechowywanym adresem

    return 0;
}

W przypadku normalnych (innych niż stałe) wskaźników możemy zmienić zarówno to, na co wskazuje wskaźnik (poprzez przypisanie wskaźnikowi nowego adresu do przechowywania), jak i zmienić wartość pod przechowywanym adresem (przypisując nową wartość do wyłuskanego wskaźnika).

Co się jednak stanie, jeśli wartość, na którą chcemy wskazać, to const?

int main()
{
    const int x { 5 }; // x jest teraz const
    int* ptr { &x };   // błąd kompilacji: nie można przekonwertować ze const int* na int*

    return 0;
}

Powyższy fragment nie zostanie skompilowany — nie możemy ustawić normalnego wskaźnika tak, aby wskazywał na zmienną const. Ma to sens: zmienna const to taka, której wartości nie można zmienić. Zezwolenie programiście na ustawienie wskaźnika innego niż stały na wartość stałą umożliwiłoby programiście wyłuskanie wskaźnika i zmianę wartości. Naruszyłoby to stałość zmiennej.

Wskaźnik na wartość stałą

wskaźnik do wartości stałej (czasami nazywany pointer to const w skrócie) jest (niestałym) wskaźnikiem, który wskazuje na wartość stałą.

Aby zadeklarować wskaźnik na wartość stałą, użyj słowa kluczowego const przed typem danych wskaźnika:

int main()
{
    const int x{ 5 };
    const int* ptr { &x }; // OK: ptr wskazuje na „const int”

    *ptr = 6; // niedozwolone: nie możemy zmienić wartości stałej

    return 0;
}

W powyższym przykładzie ptr wskazuje na const int. Ponieważ wskazywanym typem danych jest const, nie można zmienić wskazywanej wartości.

Jednakże ponieważ wskaźnik do const sam w sobie nie jest const (po prostu wskazuje na wartość const), możemy zmienić to, na co wskazuje wskaźnik, przypisując mu nowy adres:

int main()
{
    const int x{ 5 };
    const int* ptr { &x }; // ptr wskazuje na const int x

    const int y{ 6 };
    ptr = &y; // OK: ptr wskazuje teraz na const int y

    return 0;
}

Podobnie jak odwołanie do const, wskaźnik do const może również wskazywać na zmienne inne niż const. Wskaźnik do const traktuje wskazywaną wartość jako stałą, niezależnie od tego, czy obiekt pod tym adresem był początkowo zdefiniowany jako const, czy nie:

int main()
{
    int x{ 5 }; // non-const
    const int* ptr { &x }; // ptr wskazuje na „const int”

    *ptr = 6;  // niedozwolone: ptr wskazuje na „const int”, więc nie możemy zmienić wartości ptr
    x = 6; // dozwolony: wartość jest nadal non-const przy dostępie poprzez niestały identyfikator x

    return 0;
}

Wskaźniki const

Możemy również ustawić sam wskaźnik jako stały. const wskaźnik jest wskaźnikiem, którego adresu nie można zmienić po inicjalizacji.

Aby zadeklarować wskaźnik const, użyj słowa kluczowego const po gwiazdce w deklaracji wskaźnika:

int main()
{
    int x{ 5 };
    int* const ptr { &x }; // const po gwiazdce oznacza, że jest to wskaźnik const

    return 0;
}

W powyższym przypadku ptr jest stałym wskaźnikiem do (innej niż stała) wartości int.

Podobnie jak zwykła zmienna const, wskaźnik const musi zostać zainicjowany przy definicji i tej wartości nie można zmienić za pomocą przypisanie:

int main()
{
    int x{ 5 };
    int y{ 6 };

    int* const ptr { &x }; // ok: wskaźnik const jest inicjowany na adres x
    ptr = &y; // błąd: po zainicjowaniu wskaźnik const nie może zostać zmieniony.

    return 0;
}

Jednakże, ponieważ wskazywana wartości nie jest stała, możliwa jest zmiana wskazywanej wartości poprzez wyłuskanie wskaźnika const:

int main()
{
    int x{ 5 };
    int* const ptr { &x }; // ptr zawsze będzie wskazywał x

    *ptr = 6; // OK: wskazywana wartość nie jest stała

    return 0;
}

Wskaźnik const na wartość stałą

Na koniec możliwe jest zadeklarowanie const wskaźnik do wartości stałej używając słowa kluczowego const zarówno przed typem, jak i po gwiazdka:

int main()
{
    int value { 5 };
    const int* const ptr { &value }; // a const wskaźnik do const wartość

    return 0;
}

Nie można zmienić adresu stałego wskaźnika na wartość stałą, ani też wartości, którą wskazuje, nie można zmienić za pomocą wskaźnika. Można go wyłuskać jedynie w celu uzyskania wartości, na którą wskazuje.

Podsumowanie wskaźników i stałych

Podsumowując, wystarczy zapamiętać tylko 4 reguły, a są one całkiem logiczne:

  • Wskaźnikowi niebędącemu stałym (np. int* ptr) można przypisać inny adres, aby zmienić to, na co wskazuje.
  • A wskaźnik const (np. int* const ptr) zawsze wskazuje na ten sam adres i adresu tego nie można zmienić.

  • Wskaźnik na wartość inną niż stała (np. int* ptr) może zmienić wartość, na którą wskazuje. Nie mogą one wskazywać na wartość stałą.
  • Wskaźnik na wartość stałą (np. const int* ptr) traktuje wartość jako stałą, gdy jest dostępny za pośrednictwem wskaźnika i dlatego nie może zmienić wartości, na którą wskazuje. Można je wskazać na stałe lub niestałe wartości l (ale nie na wartości r, które nie mają adresu).

Utrzymanie prostej składni deklaracji może być pewnym wyzwaniem:

  • const zanim gwiazdka (np. const int* ptr) zostanie powiązana ze wskazywanym typem. Jest to zatem wskaźnik do wartości stałej i wartości tej nie można modyfikować poprzez wskaźnik.
  • const po gwiazdce (np. int* const ptr) jest ona powiązana z samym wskaźnikiem. Dlatego temu wskaźnikowi nie można przypisać nowego adresu.
int main()
{
    int v{ 5 };
   
    int* ptr0 { &v };             // wskazuje na „int”, ale sama w sobie nie jest stałą. Możemy modyfikować wartość lub adres.
    const int* ptr1 { &v };       // wskazuje na „const int”, ale sama w sobie nie jest stałą. Możemy jedynie modyfikować adres.
    int* const ptr2 { &v };       // wskazuje na „int” i sam w sobie jest const. Możemy jedynie modyfikować wartość.
    const int* const ptr3 { &v }; // wskazują na „const int” i same w sobie są const. Nie możemy modyfikować wartości ani adresu.

    // jeśli const znajduje się po lewej stronie *, const należy do wartość
    // jeśli const znajduje się po prawej stronie *, const należy do wskaźnika

    return 0;
}
guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
223 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze