Wskaźniki to koncepcja w programowaniu, która odnosi się do obiektów przechowujących adresy pamięci innych zmiennych lub zasobów. Dzięki wskaźnikom można uzyskać dostęp do danych w pamięci oraz manipulować nimi w bardziej elastyczny sposób. Termin ''wskaźnik'' pochodzi od tego, że wskazuje on na konkretny obszar pamięci, umożliwiając programiście operowanie na danych znajdujących się poza bieżącym kontekstem programu.
Wskaźniki są kluczowym elementem w zarządzaniu pamięcią, ponieważ pozwalają na dynamiczne tworzenie, modyfikowanie i zwalnianie zasobów w trakcie działania programu. Są szeroko stosowane w różnych obszarach, takich jak praca z dynamicznymi strukturami danych, przekazywanie dużych obiektów do funkcji bez kopiowania, a także w optymalizacjach wydajności.
Wskaźnik działa poprzez przechowywanie adresu pamięci. W programie operacje na wskaźnikach zazwyczaj obejmują:
Deklarację wskaźnika - wskazuje typ danych, na które wskaźnik może ''wskazywać''.
Inicjalizację wskaźnika - przypisanie do wskaźnika adresu konkretnej zmiennej lub zasobu.
Dereferencję wskaźnika - dostęp do wartości znajdującej się pod adresem, na który wskazuje wskaźnik.
Modyfikację wskaźnika - zmianę adresu pamięci, na który wskazuje, lub zmianę danych pod wskazanym adresem.
Adresowanie pamięci - wskaźnik przechowuje numer konkretnej komórki pamięci, co umożliwia szybki dostęp do danych.
Dereferencja - umożliwia odczytanie lub zmianę wartości znajdującej się w pamięci pod wskazanym adresem.
Zarządzanie dynamiczne - pozwala na alokację i dealokację pamięci w czasie działania programu, co jest kluczowe przy tworzeniu struktur danych takich jak listy, drzewa czy grafy.
Dynamiczne struktury danych - wskaźniki są niezbędne do implementacji struktur takich jak listy jednokierunkowe, stosy czy drzewa binarne, gdzie węzły są połączone za pomocą wskaźników.
Przekazywanie danych do funkcji - wskaźniki umożliwiają przekazywanie dużych struktur danych do funkcji bez konieczności ich kopiowania, co oszczędza czas i pamięć.
Niskopoziomowe operacje - wskaźniki pozwalają na bezpośrednią manipulację pamięcią, co jest przydatne w programowaniu systemowym lub sterownikach.
Wyobraźmy sobie sytuację, w której mamy zmienną przechowującą wartość, np. liczbę całkowitą. Wskaźnik na tę zmienną zapisuje adres pamięci, pod którym ta liczba się znajduje. Dzięki wskaźnikowi możemy odczytać lub zmienić wartość liczby, operując bezpośrednio na jej adresie.
Źródło: Jakub Piskorowski
Na powyższym obrazku przedstawiono dwie tabele: jedna opisuje zmienne, a druga odpowiadające im wskaźniki.
Każda zmienna (np. a
, b
, c
, d
) ma swoją wartość i jest przechowywana w określonym adresie pamięci. Wskaźniki (np. *wskA
, *wskB
) przechowują adresy tych zmiennych. Dzięki temu wskaźnik może ''wskazywać'' na zmienną, umożliwiając dostęp do jej wartości lub jej modyfikację bezpośrednio przez adres w pamięci.
Na przykład:
Zmienna "a"
ma wartość 20
i jest przechowywana pod adresem "0xac5fbffd5c"
.
Wskaźnik *wskA
przechowuje adres &a
, czyli "0xac5fbffd5c"
, co pozwala na odczytanie lub zmianę wartości zmiennej "a"
poprzez wskaźnik.
Strzałki między tabelami pokazują powiązanie między wskaźnikami a zmiennymi, które wskazują. Dzięki temu mechanizmowi wskaźniki umożliwiają bardziej elastyczne zarządzanie pamięcią i manipulację danymi w programowaniu.
Wskaźniki to potężne narzędzie w programowaniu, które pozwala na precyzyjne zarządzanie pamięcią oraz efektywne operowanie na danych. Choć wymagają ostrożności i znajomości zasad działania pamięci, ich zastosowanie pozwala na tworzenie bardziej wydajnych i elastycznych aplikacji.
Wskaźniki w języku C++ to zmienne, które przechowują adresy pamięci innych zmiennych.
Składnia:
typ *nazwaWskaznika;
Wskaźnik deklaruje się poprzez umieszczenie gwiazdki (*
) przed nazwą zmiennej.
Przykład deklaracji i inicjalizacji wskaźnika:
int liczba = 10;
int *wskaznik = &liczba;
Zmienna "liczba" przechowuje wartość 10
.
Wskaźnik "wskaznik" przechowuje adres pamięci zmiennej "liczba", który uzyskujemy za pomocą operatora &.
Wskaźnik przechowuje adres zmiennej, do której wskazuje. Aby uzyskać dostęp do wartości, do której odnosi się wskaźnik, używamy operatora dereferencji (*
).
Przykład użycia wskaźników:
int liczba = 10;
int *wskaznik = &liczba;
cout << "Wartosc zmiennej liczba: " << liczba << endl;
cout << "Adres zmiennej liczba: " << &liczba << endl;
cout << "Wartosc przechowywana w wskazniku: " << wskaznik << endl;
cout << "Wartosc wskazywana przez wskaznik: " << *wskaznik << endl;
Działanie programu:
liczba przechowuje wartość 10.
&liczba to adres pamięci zmiennej liczba.
wskaznik przechowuje adres &liczba.
*wskaznik daje dostęp do wartości 10, na którą wskazuje wskaźnik.
Przykład modyfikacji wartości przez wskaźnik:
int liczba = 10;
int *wskaznik = &liczba;
*wskaznik = 20; // Zmiana wartości zmiennej liczba przez wskaźnik
cout << "Nowa wartosc zmiennej liczba: " << liczba << endl;
W wyniku zmiany wartości przez wskaźnik, liczba zostanie ustawiona na 20.
1. Deklaracja wskaźnika:
int *ptr;
2. Inicjalizacja wskaźnika:
ptr = &zmienna;
3. Dereferencja wskaźnika:
Dostęp do wartości zmiennej, na którą wskazuje wskaźnik:
*ptr = 5; // Ustawia wartość zmiennej na 5
4. Nullowanie wskaźnika:
Wskaźnik, który nie wskazuje na żadną zmienną:
int *ptr = nullptr;
Program demonstruje, jak wskaźnik przechowuje adres zmiennej, umożliwia modyfikację jej wartości oraz jak korzystać z operatorów adresu (&
) i dereferencji (*
).
int liczba = 10; // Deklaracja i inicjalizacja zmiennej
int *wskaznik = &liczba; // Przypisanie adresu zmiennej liczba do wskaznika
// Wyswietlanie informacji o zmiennej i wskazniku
cout << "Wartosc zmiennej liczba: " << liczba << endl;
cout << "Adres zmiennej liczba: " << &liczba << endl;
cout << "Wartosc przechowywana w wskazniku: " << wskaznik << endl;
cout << "Wartosc wskazywana przez wskaznik: " << *wskaznik << endl;
// Zmiana wartosci zmiennej przez wskaznik
*wskaznik = 20;
cout << "Nowa wartosc zmiennej liczba (po zmianie przez wskaznik): " << liczba << endl;
Działanie:
Deklaracja zmiennej: Na początku programu deklarowana jest zmienna "liczba" i przypisana do niej wartość 10.
Przypisanie wskaźnika: Wskaźnik "wskaznik" przechowuje adres pamięci zmiennej "liczba", uzyskany za pomocą operatora adresu &.
Wyświetlanie informacji: Program wyświetla wartość zmiennej "liczba", jej adres w pamięci, wartość przechowywaną w wskaźniku oraz wartość wskazywaną przez wskaźnik za pomocą operatora dereferencji *.
Modyfikacja wartości: Wskaźnik umożliwia modyfikację wartości zmiennej "liczba". Zmiana wartości przez wskaźnik (*wskaznik = 20) powoduje, że zmienna "liczba" przyjmuje nową wartość 20.
Wynik działania programu:
Wartosc zmiennej liczba: 10
Adres zmiennej liczba: 0x7ffee3c64ba4
Wartosc przechowywana w wskazniku: 0x7ffee3c64ba4
Wartosc wskazywana przez wskaznik: 10
Nowa wartosc zmiennej liczba (po zmianie przez wskaznik): 20
Podsumowanie:
W tym przykładzie zademonstrowano, jak wskaźniki przechowują adresy zmiennych i umożliwiają ich modyfikację. Dzięki operatorowi dereferencji można uzyskać dostęp do wartości wskazywanej przez wskaźnik, a także ją zmieniać. Mechanizm ten jest podstawą zaawansowanych operacji na danych w języku C++.
W tym przykładzie zademonstrowano, jak przekazywać argumenty do funkcji za pomocą wskaźników. Dzięki temu funkcja może modyfikować wartość zmiennej przekazanej jako argument.
#include <iostream>
using namespace std;
// Funkcja modyfikujaca wartosc zmiennej za pomoca wskaznika
void ZmienWartosc(int *wskaznik) {
*wskaznik = *wskaznik * 2; // Podwojenie wartosci wskazywanej przez wskaznik
}
int main() {
int liczba = 15; // Deklaracja zmiennej
cout << "Wartosc przed wywolaniem funkcji: " << liczba << endl;
ZmienWartosc(&liczba); // Przekazanie adresu zmiennej do funkcji
cout << "Wartosc po wywolaniu funkcji: " << liczba << endl;
return 0;
}
Działanie:
Deklaracja funkcji: Funkcja ZmienWartosc przyjmuje wskaźnik jako argument. W jej ciele następuje modyfikacja wartości zmiennej, której adres został przekazany do funkcji.
Przekazanie adresu zmiennej: W funkcji main adres zmiennej liczba jest przekazywany do funkcji ZmienWartosc za pomocą operatora adresu (&).
Modyfikacja wartości: Funkcja ZmienWartosc modyfikuje wartość zmiennej poprzez wskaźnik, podwajając jej wartość (*wskaznik = *wskaznik * 2).
Wyświetlanie wartości: Przed i po wywołaniu funkcji wyświetlana jest wartość zmiennej liczba, co pozwala zobaczyć efekt działania wskaźników.
Wynik działania programu:
Wartosc przed wywolaniem funkcji: 15
Wartosc po wywolaniu funkcji: 30
Podsumowanie:
Ten przykład pokazuje, jak wykorzystać wskaźniki do przekazywania argumentów do funkcji w celu ich modyfikacji. Mechanizm ten jest szczególnie przydatny w przypadku operacji wymagających bezpośredniego wpływu na dane, takich jak zmiana wartości zmiennych w funkcji lub współdzielenie pamięci pomiędzy różnymi funkcjami.
W poniższym przykładzie pokazano, jak wskaźniki w języku C++ mogą być używane do iteracji po elementach dynamicznej tablicy oraz modyfikacji jej wartości.
#include <iostream>
using namespace std;
// Funkcja zwiekszajaca wartosci elementow tablicy o 1
void ZwiekszElementy(int *tablica, int rozmiar) {
for (int i = 0; i < rozmiar; i++) {
tablica[i] += 1; // Modyfikacja elementow tablicy
// *(tablica + i) += 1; // Alternatywnie, dostep do elementu za pomoca wskaznika
}
}
int main() {
int rozmiar;
// Pobranie rozmiaru tablicy od uzytkownika
cout << "Podaj rozmiar tablicy: ";
cin >> rozmiar;
// Dynamiczna alokacja pamieci dla tablicy
int *liczby = new int[rozmiar];
// Wypelnianie tablicy wartosciami poczatkowymi
cout << "Podaj " << rozmiar << " elementow tablicy:" << endl;
for (int i = 0; i < rozmiar; i++) {
cin >> liczby[i];
}
cout << "Tablica przed wywolaniem funkcji:" << endl;
for (int i = 0; i < rozmiar; i++) {
cout << liczby[i] << " ";
}
cout << endl;
ZwiekszElementy(liczby, rozmiar); // Przekazanie dynamicznej tablicy do funkcji
cout << "Tablica po wywolaniu funkcji:" << endl;
for (int i = 0; i < rozmiar; i++) {
cout << liczby[i] << " ";
}
cout << endl;
// Zwolnienie pamieci
delete[] liczby;
return 0;
}
Działanie:
Dynamiczna alokacja pamięci: Program dynamicznie tworzy tablicę o rozmiarze podanym przez użytkownika za pomocą operatora new. Tablica jest przechowywana w pamięci dynamicznej, a wskaźnik liczby wskazuje na jej pierwszy element.
Wypełnianie tablicy: Użytkownik wprowadza wartości, które są zapisywane w kolejnych komórkach dynamicznej tablicy.
Przekazanie tablicy do funkcji: Tablica dynamiczna jest przekazywana do funkcji ZwiekszElementy jako wskaźnik do pierwszego elementu tablicy.
Iteracja po tablicy: Funkcja ZwiekszElementy iteruje po elementach tablicy za pomocą wskaźnika i zwiększa każdą wartość o 1.
Wyświetlanie tablicy: Program wyświetla zawartość tablicy przed i po modyfikacji.
Zwolnienie pamięci: Po zakończeniu pracy dynamiczna pamięć alokowana dla tablicy jest zwalniana za pomocą delete[], aby uniknąć wycieków pamięci.
Wynik działania programu:
Podaj rozmiar tablicy: 5
Podaj 5 elementów tablicy:
1
2
3
4
5
Tablica przed wywołaniem funkcji:
1 2 3 4 5
Tablica po wywołaniu funkcji:
2 3 4 5 6
Dynamiczna tablica w C++ to struktura danych, której rozmiar można określić w trakcie działania programu. Pamięć na dynamiczną tablicę jest przydzielana za pomocą operatora new, co pozwala na elastyczne zarządzanie rozmiarem danych.
Zalety dynamicznej tablicy:
Rozmiar tablicy nie musi być znany w momencie kompilacji.
Umożliwia efektywne wykorzystanie pamięci poprzez dostosowanie jej rozmiaru do potrzeb programu.
Wady dynamicznej tablicy:
Programista musi samodzielnie zarządzać pamięcią - przydzieloną pamięć należy zwolnić za pomocą delete[], aby uniknąć wycieków pamięci.
Operacje na dynamicznej pamięci są bardziej złożone i wymagają dodatkowej uwagi.
Podsumowanie:
Dynamiczna alokacja pamięci umożliwia tworzenie elastycznych struktur danych, takich jak tablice o zmiennym rozmiarze. W powyższym przykładzie wskaźniki zostały wykorzystane do iteracji po elementach tablicy oraz modyfikacji ich wartości. Dzięki operatorowi delete[] tablica dynamiczna została poprawnie zwolniona, co zapobiega wyciekowi pamięci.
Napisz program, który umożliwia pracę ze wskaźnikami na zmienne. Program będzie wykonywał operacje na wskaźnikach i zmiennych, a użytkownik będzie miał możliwość modyfikowania wartości zmiennych bezpośrednio przez wskaźniki.
Kroki do wykonania:
Zainicjuj dwie zmienne typu całkowitego: a
i b
.
Zainicjuj wskaźniki, które będą wskazywać na te zmienne.
Wypisz wartości zmiennych przed modyfikacją.
Zmodyfikuj wartości zmiennych przy użyciu wskaźników.
Wypisz zmodyfikowane wartości zmiennych po modyfikacji.
Wynik działania programu:
Przed modyfikacją:
a = 5
b = 10
Po modyfikacji:
a = 15
b = 20
Napisz program, który operuje na tablicy z wstępnie zdefiniowanymi 10 wartościami typu float, a następnie przekazuje je do funkcji za pomocą wskaźników w celu znalezienia największej i najmniejszej wartości. Wyniki należy zwrócić do głównego programu i wyświetlić.
Tablica zawiera następujące wartości:
1.5, -3.2, 4.8, 7.0, -1.1, 0.0, 3.3, -6.6, 8.9, 2.2.
Wymagania:
Funkcja FindMinMax:
Przyjmuje wskaźnik do tablicy liczb typu float oraz jej rozmiar.
Znajduje największą i najmniejszą wartość w tablicy.
Ustawia wartości największej i najmniejszej liczby w zmiennych max i min, przekazanych jako wskaźniki.
W funkcji main:
Należy stworzyć zmienne max i min, które zostaną ustawione przez funkcję FindMinMax.
Wyniki, czyli największą i najmniejszą wartość, wyświetl w funkcji main.
Kroki do wykonania:
Zdefiniuj tablicę float o 10 elementach, wypełnioną podanymi wartościami.
Przekaż tablicę, jej rozmiar oraz wskaźniki do zmiennych max i min do funkcji FindMinMax.
W funkcji FindMinMax znajdź największą i najmniejszą wartość w tablicy.
Zwróć wyniki do głównego programu przez wskaźniki.
Wyświetl największą i najmniejszą wartość w funkcji main.
Wynik działania programu:
Tablica: 1.5 -3.2 4.8 7 -1.1 0 3.3 -6.6 8.9 2.2
Najwieksza liczba: 8.9
Najmniejsza liczba: -6.6
Napisz program, który wykorzystuje wskaźniki do manipulacji tablicą oraz obliczania sumy elementów w tablicy za pomocą dedykowanej funkcji. Program powinien umożliwiać użytkownikowi zmianę wartości elementów tablicy i ponowne obliczenie sumy.
Kroki do wykonania:
Zainicjuj tablicę liczb całkowitych z kilkoma wartościami.
Oblicz sumę elementów tablicy, wywołując funkcję ObliczSume.
Wyświetl sumę elementów tablicy.
Umożliw użytkownikowi zmianę wartości elementów tablicy.
Ponownie oblicz sumę elementów tablicy przy użyciu funkcji ObliczSume.
Wyświetl zmodyfikowaną tablicę oraz nową sumę.
Wynik działania programu:
Suma elementow tablicy: 50
Podaj nowa wartosc dla elementu 0: 10
Podaj nowa wartosc dla elementu 1: 20
Podaj nowa wartosc dla elementu 2: 30
Podaj nowa wartosc dla elementu 3: 40
Nowa tablica:
10 20 30 40
Nowa suma elementow tablicy: 100
Napisz program, który operuje na tablicy z 10 wylosowanymi liczbami całkowitymi z przedziału od 0 do 50, a następnie umożliwia użytkownikowi zgadywanie jednej z tych liczb. Liczba podana przez użytkownika oraz tablica są przekazywane do funkcji za pomocą wskaźników w celu sprawdzenia, czy liczba znajduje się w tablicy. Program informuje użytkownika, czy zgadł liczbę, i kontynuuje, aż użytkownik poda poprawną liczbę. Po zakończeniu zgadywania wyświetlana jest liczba prób.
Wymagania:
Funkcja LosujTablice:
Wypełnia tablicę liczbami losowymi z przedziału od 0 do 50.
Funkcja SprawdzLiczbe:
Przyjmuje wskaźnik do liczby podanej przez użytkownika oraz wskaźnik do tablicy.
Sprawdza, czy liczba znajduje się w tablicy.
Zwraca wynik (true lub false).
W funkcji main:
Należy stworzyć tablicę o 10 elementach, wypełnioną liczbami wygenerowanymi przez funkcję LosujTablice.
Umożliwić użytkownikowi podawanie liczby.
Wykorzystać funkcję SprawdzLiczbe do sprawdzenia, czy liczba znajduje się w tablicy.
Powtarzać pytanie, dopóki użytkownik nie zgadnie liczby.
Wyświetlić informację o liczbie prób.
Kroki do wykonania:
Zdefiniuj tablicę liczb całkowitych o 10 elementach i wypełnij ją liczbami losowymi za pomocą funkcji LosujTablice.
W funkcji main zapytaj użytkownika o liczbę.
Przekaż wskaźnik do liczby użytkownika oraz wskaźnik do tablicy do funkcji SprawdzLiczbe.
Powtarzaj proces, aż użytkownik zgadnie liczbę.
Po zgadnięciu wyświetl liczbę prób.
Wynik działania programu:
Tablica: 20, 27, 26, 44, 29, 50, 14, 11, 24, 14,
Podaj liczbe: 15
Nie zgadles
Podaj liczbe: 20
Zgadles!
Zgadles za 2 razem.
Rozbuduj program z poprzedniego zadania o dodatkowe funkcjonalności, które umożliwią użytkownikowi większą kontrolę nad losowaniem liczb oraz ich sprawdzaniem.
Nowe wymagania:
Użytkownik podaje, ile liczb chce wylosować.
Użytkownik określa przedział liczb (wartość początkową i końcową), z którego mają być losowane liczby.
Program losuje liczby na podstawie wprowadzonych danych i zapisuje je w tablicy.
Program działa na zasadzie gry:
Użytkownik podaje liczby, które są sprawdzane pod kątem ich obecności w tablicy.
Gra trwa, aż użytkownik trafi jedną z wylosowanych liczb.
Po trafieniu programu wyświetla, ile prób było potrzebnych do zgadnięcia liczby.
Wskazówka:
Do generowania liczb losowych w określonym przedziale możesz użyć wzoru:
losowa_liczba = poczatek + rand() % (koniec - poczatek + 1);
Wynik działania programu:
Wartosc poczatkowa: 10
Wartosc koncowa: 60
Tablica: 22, 54, 36, 28, 55, 12, 19, 49, 40, 16
Podaj liczbe: 20
Nie zgadles
Podaj liczbe: 28
Zgadles!
Zgadles za 2 razem.