• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

nauka wskaźników na przykładzie - problem, jak to zrozumieć?

VPS Starter Arubacloud
+1 głos
598 wizyt
pytanie zadane 8 maja 2017 w C i C++ przez andrut Użytkownik (870 p.)

Chciałbym zrozumieć zagadnienie wskaźników w C++, próbuję to zrobić na prostych przykładach, które sam modyfikuję. Nie wiem czy tędy droga, ale wydaje mi się, że to dobry pomysł.
Już na początku natrafiłem na kilka znaków zapytania... Może ktoś mi łopatologicznie wyjaśni o co chodzi w tych konkretnych miejscach w kodzie?

#include <iostream>
#include <stdio.h>
using namespace std;
int main()
  {
    int zmienna;
    int *wskaznik;
    wskaznik = &zmienna;

    cout << wskaznik << endl; 		//pokazuje adres zmiennej zmienna
    zmienna = 22;
    cout << wskaznik << endl;
    cout << *wskaznik << endl; 		//wyswietla wartosc zmiennej
    cout << wskaznik << endl;
    cout << *wskaznik+3 << endl; 	//co ja tu zwiekszam o 3?
    cout << zmienna << endl; 		//bo nie wartosc zmiennej...
    cout << wskaznik << endl;
    cout << *wskaznik-1 << endl; 	//co sie dzieje tutaj?
    cout << wskaznik << endl;
    cout << *wskaznik-- << endl; 	//i co tutaj?

    getchar(); getchar();
    return 0;
  }

 

6 odpowiedzi

+1 głos
odpowiedź 12 maja 2017 przez WilmaOlcho Obywatel (1,740 p.)
wybrane 12 maja 2017 przez andrut
 
Najlepsza
cout << wskaznik << endl;       //pokazuje adres zmiennej zmienna

    zmienna = 22;

    cout << wskaznik << endl;

    cout << *wskaznik << endl;      //wyswietla wartosc zmiennej

    cout << wskaznik << endl;

    cout << *wskaznik+3 << endl;    //co ja tu zwiekszam o 3? //Nic, wyświetlasz wartość zmiennej zwiększoną o 3

    cout << zmienna << endl;        //bo nie wartosc zmiennej... // No właśnie, proponowałbym *wskaznik+=3

    cout << wskaznik << endl;

    cout << *wskaznik-1 << endl;    //co sie dzieje tutaj? //Tak samo jak przedtem, wyswietlasz zmienna-1

    cout << wskaznik << endl;

    cout << *wskaznik-- << endl;    //i co tutaj? //Postdekrementacja zmiennej zmienna

Stąd kilka wniosków na początek:
wskaznik=&zmienna; //Wskaźnik na zmienną (Wskaźnik to przechowanie adresu pod którym znajduje się zmienna)

wskaznik++; //To jest kolejna komórka pamięci dalej, w przypadku tablicy po prostu wskażesz następną wartość
//W innym przypadku działanie programu może zostać powstrzymane i go wysypiesz, w kazdym razie jeżeli nie wiesz co robisz, możesz w ten sposób w najlepszym przypadku spowodować niestabilne działanie programu.
*wskaznik+=5; //Dodajesz 5 do zmiennej
wskaznik=nullptr; //Wskaznik na nic nie wskazuje
*wskaznik*=*wskaznik; //mnożysz zmienną przez siebie, niby same gwiazdki, ale w różnych miejscach mają różne znaczenia
//&zmienna to adres zmiennej
//wskaznik przechowuje dokładnie ten adres kiedy mu ten adres przypisujesz :)
komentarz 12 maja 2017 przez andrut Użytkownik (870 p.)

@WilmaOlcho napisałaś:

" wskaznik++; //To jest kolejna komórka pamięci dalej, w przypadku tablicy po prostu wskażesz następną wartość"
Tego mi najbardziej w tym wyjaśnieniu brakowało! Czyli po prostu nie ma sensu inkrementować czy dekrementować wskaźnika, jeśli nie odnosi się on do tablicy, a jest zwykłą zmienną. Stąd mi się brały te dziwne wartości...

Dzięki yes

 

+2 głosów
odpowiedź 8 maja 2017 przez tangarr Mędrzec (154,780 p.)
edycja 8 maja 2017 przez tangarr

*wskaznik służy do pobrania wartości wskazywanej zmiennej.
*wskaznik+3 jest równoważny zapisowi zmienna+3, dodajesz 3 do wartości zmiennej, ale nie modyfikujesz zmiennej (tak samo dla *wskaznik-1).
(edit: poprawiam odpowiedź zgodnie z komentarzem)
wskaznik-- "przesuwa" wskaźnik do tyłu (zmniejsza wskazywany adres) o tyle bajtów jaki rozmiar ma wskazywany typ. Używa się tego do iterowania po tablicach (w przód i tył).
*wskaznik-- zmniejsza wartość wskazywanej zmiennej o 1 (równoważne z zmienna-- lub zmienna=zmienna-1)

komentarz 8 maja 2017 przez Munvik Dyskutant (9,350 p.)

pomyłka w ostatnim przykładzie

*wskaznik--  nie przesuwa wskaźnika do tyłu tylko dekrementuje zmienną wskazywaną

komentarz 8 maja 2017 przez tangarr Mędrzec (154,780 p.)
Oj racja, racja, nie spojrzałem na gwiazdkę i się zbłaźniłem :P
komentarz 8 maja 2017 przez unknown Nałogowiec (39,560 p.)

*wskaznik-- zmniejsza wartość wskazywanej zmiennej o 1 

Ja za to proponuję sprawdzić co ma większy priorytet: operator dereferncji czy operator dekrementacji. 

komentarz 8 maja 2017 przez andrut Użytkownik (870 p.)

@tangarr, @Munvik - dzięki za wskazania, jednak nie wszystko mi się zgadza, coś z tym ostatnim jest nie tak...
@unknown - co masz dokładnie na myśli? Dekrementacja przyrostkowa ma pierwszeństwo przed operatorem * pośredniości, ale przyznam, że nie wiem czy o to chodzi.

Kiedy zrobiłem modyfikację kodu, to coś z tą dekrementacją jest nie tak:

#include <iostream>
#include <stdio.h>
using namespace std;
int main()
  {
    int zmienna;
    int *wskaznik;
    wskaznik = &zmienna;

    zmienna = 22;
    cout << zmienna << endl;        //1. wiersz: 22
    cout << *wskaznik << endl;      //2. wiersz: 22
    *wskaznik = *wskaznik-1;
    cout << *wskaznik << endl;      //3. wiersz: 21
    cout << zmienna << endl;        //4. wiersz: 21
    zmienna--;
    cout << zmienna << endl;        //5. wiersz: 20
    *wskaznik--;
    // cout << *wskaznik-- << endl;
    cout << *wskaznik << endl;      //6. wiersz: 4309824
 
    getchar(); getchar();
    return 0;
  }

Skąd ta wartość 4309824?

komentarz 8 maja 2017 przez unknown Nałogowiec (39,560 p.)

Operator post-dekrementacji ma wyższy priorytet niż operator dereferncji. Czyli w tym przypadku:

cout << *wskaznik-- << endl;    //i co tutaj?

następuje dereferncja poprawnej wartości (ze względu na to jak działa post-dekrementacja), ale przy następnej dereferncji wskaźnik nie wskazuje już na poprawny adres co powoduje UB.

komentarz 8 maja 2017 przez andrut Użytkownik (870 p.)
edycja 8 maja 2017 przez andrut

Myślałem, że to:
*wskaznik = *wskaznik-1;
i to:
*wskaznik--;
jest równoważne... czyli, że zmniejsza wartość o 1.

Oraz sądziłem, że używając gwiazdki przed nazwą wskaźnika nie modyfikuję adresu zmiennej... Tym bardziej, że wartość, która się tu pojawia jest dziesiętna, a nie szesnastkowa, więc nie może to być adres w pamięci...

komentarz 8 maja 2017 przez unknown Nałogowiec (39,560 p.)

http://en.cppreference.com/w/cpp/language/operator_precedence

To:

*wskaznik--;

jest równoważne:

*(wskaznik--);

a to:

*wskaznik = *wskaznik-1;

jest równoważne:

*wskaznik = (*wskaznik)-1;

 

+2 głosów
odpowiedź 8 maja 2017 przez Munvik Dyskutant (9,350 p.)
edycja 8 maja 2017 przez Munvik

Bo widzisz, wskaźnikiem operujesz na konkretnej zmiennej.

int liczba = 3;
int *wskaznik = &liczba;

std::cout << liczba << std::endl; // pokaże 3
std::cout << wskaznik << std::endl;  // pokaże adres zmiennej liczba
std::cout << *wskaznik << std::endl; // pokaże to co jest pod zmienną liczba, czyli 3
std::cout << *wskaznik + 3 << std::endl; // pokaże to co jest pod zmienną liczba + 3, czyli 3 +3 = 6,
// zwróć uwagę, że nie zmieniamy wartości liczba tylko wyświetlamy (liczba + 3)

std::cout << liczba + 3 << std::endl; // pokaże to co jest w zmiennej liczba + 3, czyli 3 + 3 = 6
// rownoważne z poprzednim zapisem

++*wskaźnik; // inkrementujemy zmienną liczba, teraz zmienna liczba = 4

komentarz 8 maja 2017 przez Wiciorny Ekspert (269,120 p.)
*wskaźnik++; // inkrementujemy zmienną liczba, teraz zmienna liczba = 4

niestety tak nie jest. 

1
komentarz 8 maja 2017 przez Munvik Dyskutant (9,350 p.)
Racja, preinkrementacja tylko działa, ale dlaczego ?
komentarz 8 maja 2017 przez Wiciorny Ekspert (269,120 p.)
też byłem zdziwiony, bo podobnie myślałem. Przeanalizuje to
+2 głosów
odpowiedź 8 maja 2017 przez Wiciorny Ekspert (269,120 p.)

Powiem Coś od siebie:

ogólnie wskaźnik sam w sobie jest adresem komórki, jakby szufladki " na coś".  Np do tej szufladki możesz schować liczbę, jakiś napis etc.

Wyobraź sobie masz szafe: gdzie jest mega dużo szufladek[ każda szufladka jest wskaźnikiem, a numer tego wskaźnika to bedzie jakby "etykieta" tej szuflady" ] - np przykleiłeś nakleiki. 

wskaźnik1   // 1 szuflada 
wskaźnik2   // 2 szuflada 

wskaźnik3 /// itd...
wskaźnik4 

teraz:

Jeśli korzystamy z operatora "*" przy wskaźniku, operujemy na tym co znajduje się w tej szufladzie, więc każde operacje np. 

*wskaźnik=1 // przypisze nam wartość do środka tej szulfadki 

*wskaźnik++// doda do tego co wśrodku jedynke ! bo mamy "*" operator. 

jeśli posługujesz się bez operatora "*" to operujesz na samej szufladzie np, przestawisz etykiete szyfladki numer "2" 

na etykiete szufladki numer "1" to tak jakbys odkleił naklejke numer 2 i wkleił ją na miejsce 1 na szuflade 1.

Więcej info- wal priw to objaśnie wszystkie niedopowiedzenia 

komentarz 8 maja 2017 przez andrut Użytkownik (870 p.)

Fajnie obrazowo wyjaśniłeś yes

*wskaznik++ nie zwiększa wartości zmiennej, a wrzuca jakąś dziką wartość...
(zobacz wyżej w komentarzach).

+2 głosów
odpowiedź 8 maja 2017 przez mokrowski Mędrzec (155,460 p.)

Zanim zaczniesz cokolwiek robić ze wskaźnikami, używaj nawiasów do jawnego określania kolejności wykonywania operacji. Czyli np:

// Pseudo kod. Uwaga w C++ kilka to UB!
int a = 42;
int * ptr = &a;

std::cout << ptr << std::endl;
std::cout << ptr + 2 << std::endl;
std::cout << (*ptr) + 2 << std::endl;
std::cout << *(ptr) + 2 << std::endl;
std::cout << *(ptr + 2) << std::endl;

Nawiasy wyjaśnią Ci kolejność wykonywania operacji. Tu przeczytasz jaką definiuje standard :-) http://en.cppreference.com/w/cpp/language/operator_precedence

Na początku (a często także i w poważnych projektach) radził bym ją stosować wszędzie. Zwróć uwagę że nawet w tym wątku były nieścisłości a nawiasy wyjaśniają jaka jest kolejność. 

–1 głos
odpowiedź 8 maja 2017 przez jpacanowski VIP (101,940 p.)
edycja 8 maja 2017 przez jpacanowski
komentarz 8 maja 2017 przez andrut Użytkownik (870 p.)

Dzięki, przejrzałem to, ale raczej nie chodziło mi o większą porcję wiedzy, ale o zrozumienie działania na konkretnym przykładzie smiley

Podobne pytania

0 głosów
1 odpowiedź 180 wizyt
0 głosów
2 odpowiedzi 235 wizyt

92,455 zapytań

141,263 odpowiedzi

319,099 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...