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

question-closed wskaźniki wskazujące na wskaźnik w C++

Object Storage Arubacloud
0 głosów
6,016 wizyt
pytanie zadane 6 sierpnia 2017 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 7 sierpnia 2017 przez Jakub 0

witam, mam dzisiaj bardzo krótkie pytanko ,mianowicie wiem że jest możliwość zrobienia wskaźnika mieszczącego adres innego wskaźnika . Analogicznie rozumiem że "musi" mieć on wyższy poziom (więcej gwiazdek) :

    int *hight;
    int liczba = 10;
    hight = &liczba;
    int **hight2 = &hight;

 

jednak zadziała mi też taki zapis :

 

int *hight3 = hight;

 

jednak kiedy zrobię tak to mi nie zadziała :

 

int *hight3 = hight2;

 

czym te zapisy się różnią ? Który lepiej stosować ? Dziękuje za pomoc i pozdrawiam 

komentarz zamknięcia: Już wiem

2 odpowiedzi

+1 głos
odpowiedź 6 sierpnia 2017 przez criss Mędrzec (172,590 p.)
wybrane 6 sierpnia 2017 przez Jakub 0
 
Najlepsza
Pierwszy zapis to pobranie adresu wskaźnika i przypisanie go do drugiego wskaźnika. hight2 wskazuje na adres pod którym znajdziemy wskaźnik hight.
W drugim zapisie po prostu przypisujesz wartość jednego wskaźnika (przetrzymywany przez niego adres) do drugiego. hight3 od teraz wskazuje na ten sam adres, co hight.

PS: jeśli przez "hight" chodziło ci o "wysokość", to poprawna pisowania to "height" :)
komentarz 6 sierpnia 2017 przez Jakub 0 Pasjonat (23,120 p.)
ale czemu mi nie zadziała to : int *hight3 = hight2; ??

dzięki za pomoc

 

*ale hight to też poprawna pisownia ,może i nie jestem mistrzem w angielskim ale sprawdzałem to wcześniej w tłumaczu czy się nie mylę ...
1
komentarz 6 sierpnia 2017 przez criss Mędrzec (172,590 p.)
Bo to są różne typy. hight3 jest typu int*, a hight2 jest typu int**. Prawda, wskaźnik to zawsze wskaźnik - po prostu liczba całkowita, ale kompilator nie pozwala na takie konwersje dla bezpieczeństwa. Jakbyś się jednak uparł, to możesz to zrobić reinterpret_cast-em.

Co do "hight" - też sprawdzałem :D Doczytałem się, że "hight" co prawda jest poprawne (albo przynajmniej kiedyś było), ale jest już przestarzałe.
komentarz 6 sierpnia 2017 przez Jakub 0 Pasjonat (23,120 p.)
dzięki za odpowiedź ,wreszcie teraz mogę w pełni zrozumieć stos
komentarz 6 sierpnia 2017 przez jpacanowski VIP (101,940 p.)
komentarz 6 sierpnia 2017 przez criss Mędrzec (172,590 p.)
@jpacanowski - tak, stąd właśnie wziąłem te informacje (przypisek 'daw.')
+1 głos
odpowiedź 6 sierpnia 2017 przez PoetaKodu Stary wyjadacz (10,990 p.)
edycja 6 sierpnia 2017 przez PoetaKodu

Podwójny wskaźnik jest czasem stosowany, by zmienić wartość samego wskaźnika. Przykład:

int x = 5;
int y = 10;

void changeToX(int **ptr)
{
    *ptr = &x;
}
void changeToY(int **ptr)
{
    *ptr = &y;
}

int main()
{
    int *x_ptr = &x;
    int *y_ptr = &y;

    changeToX(&y_ptr);
    changeToY(&x_ptr);
}

Dzięki changeToX oraz changeToY możesz zmienić wartość samego wskaźnika - możesz zmienić go tak, żeby wskazywał na coś innego. Po wykonaniu tych funkcji x_ptr będzie wskazywał na y a y_ptr będzie wskazywał na x.
Drugi zapis natomiast tworzy nowy wskaźnik i kopiuje wartość z "hight". Tworzysz dzięki temu kopię wskaźnika "hight", wszelkie. Podwójny wskaźnik to po prostu wskaźnik, który wskazuje na wskaźnik do czegoś.
Zwykły wskaźnik wskazuje na jakiś adres w pamięci a wskaźnik na wskaźnik wskazuje na sam ten wskaźnik.

Trochę to brzmi dziwnie ale w praktyce jest łatwe.

int data;
int *dataPtr = &data;
int **dataPtrPtr = &dataPtr;

int *otherPtr = &dataPtrPat; //error

A takie coś nie działa, bo typ się nie zgadza. Gdyby coś takiego działało oznaczałoby to, że int == int** (bo do int* zapisujesz adres inta a nie int**) co jest błędem. int != int** bo prawa strona to wskaźnik. Wskaźnik nie zawsze ma 4 bajty tak jak int - właściwie na 64 bitowych systemach ma on 64 bity i zapisywanie wskaźnika do inta jest błędem. 

komentarz 6 sierpnia 2017 przez Jakub 0 Pasjonat (23,120 p.)
edycja 6 sierpnia 2017 przez Jakub 0
dzięki za praktyczny przykład i wytłumaczenie ,niestety dwom osobom dnie naj jest nie możliwe D:
komentarz 6 sierpnia 2017 przez Jakub 0 Pasjonat (23,120 p.)
korzystając z okazji mam jeszcze jedno krótkie pytanie (nie opłaca się robić dla niego nowego tematu)

mianowicie znam takie coś :

int *wsk = new int; //działa

skoro utworzyłem w ten sposób nową zmienną to czemu przypisanie pod spodem mi nie zadziała ?

wsk = 12; //nie działa

może i pytanie głupie ale w przypadku obiektów takie coś było możliwe (oczywiście używając wskaźnika polimorficznego)
1
komentarz 6 sierpnia 2017 przez PoetaKodu Stary wyjadacz (10,990 p.)

Bo w ten sposób ustawiasz wskaźnik na adres 12. Nic nie stoi na przeszkodzie, by zmienić adres takiego wskaźnika:

int *ptr = new int;

Jeśli teraz zmienisz adres tego wskaźnika to bezpowrotnie utracisz dostęp do tej zmiennej, mimo że wciąż będzie ona w pamięci bo nie zostanie zwolniona poprawnie (poprzez delete ptr). No właśnie... i tutaj dochodzimy do jednej z największych bolączek wszystkich programistów. Zarządzanie pamięcią. Jedne języki oferują automatyczne zarządzanie pamięcią poprzez tzw. Garbage Collector, są to np. C#, Java, Rust, JavaScript itp. Chodzi o to, że zmienne, które zostały dynamicznie zaalokowane i nie są już używane (nie ma żadnego wskaźnika do nich) zostają automatycznie zwalniane przez "Zbieracz śmieci" (Garbage Collector). Jest to efektywne bardzo efektywne rozwiązanie, niestety (a może stety) niezaimplementowane w języki C++ (chociaż jest możliwe napisanie go samemu). Jeśli nie będziesz poprawnie zwalniał pamięci, to przez cały okres działania programu będzie ta pamięć rosła - to tak jak byś nigdy nie wyrzucał śmieci z domu, będą się gromadzić i gromadzić. Dlatego trzeba to robić skutecznie i w miarę wydajnie. 

Typowe wskaźniki w C++, o których właśnie pytania zadajesz są niebezpieczne. Nawet jeśli napiszesz taki kod np.

void someFunc()
{
    int *ptr = 0;
    *ptr = 230; // celowy crash programu - segmentation fault
}

int main()
{
    int *var = new int;
    someFunc();
    delete var;
    return 0;
}

To mimo, że możesz mieć wrażenie, że poprawnie to rozwiązałeś (bo przecież zwalniasz zaalokowaną pamięć) to tak nie jest. Jeśli tak jak właśnie w powyższym programie funkcja someFunc() rzuci wyjątek* (czyt. niżej) to "delete var" się nie wykona - występuje wyciek pamięci. W tej sytuacji akurat nic to nie zmienia, bo cała aplikacja się sypnie i przy wyjściu i tak zwolni zabraną pamięć, ale jeśli po obsłużeniu takiego wyjątku dalej będziesz chciał taki program kontynuować to pamięć rośnie i rośnie. Do chronienia się przeciwko takim przypadkom używa się inteligentnych wskaźników (smart pointers). Przed standardem C++11 był to std::auto_ptr, teraz używa się std::unique_ptr, std::weak_ptr oraz std::shared_ptr. O tym dowiesz się później, gdy zaczniesz programowanie obiektowe i będziesz całe obiekty dynamicznie alokował - wtedy jak najszybciej przeczytaj o smart pointerach.

*wyjątek - o rzucaniu wyjątków pewnie w przyszłości będziesz się uczył - w skrócie - jeśli występuje bardzo nieoczekiwana sytuacja - np. ktoś chce podzielić przez zero, to możemy rzucić wyjątek np. może nim być tekst - "Error, tried to divide by zero". W tym momencie całkowicie przerywana jest wykonywana akcja i kod programu "skacze" do góry wychodząc po kolei z każdej funkcji, w której aktualnie przebywał aż napotka blok "try...catch", który służy do przechwytywania wyjątków i wykonywania odpowiednich akcji w razie ich wystąpienia.

1
komentarz 7 sierpnia 2017 przez criss Mędrzec (172,590 p.)

Myślę, że Jakub pytał o coś znacznie prostszego.
int *p = 12; nie działa z tego samego powodu jaki opisałem w swojej odpowiedzi (właściwie to w komentarzu po twoim pytaniu do mojej odpowiedzi) - konieczność konwersji z int na int*. (12 to 'obiekt' tymczasowy typu int o wartości 12 podobnie jak 12. to double o wartości 12, a 12.f to float o wartości 12)
int *p = new int; działa, bo przypisujesz tu int* do int*. Nie potrzebna jest żadna konwersja. operator new zwraca wskaźnik do utworzonego obiektu, a nie sam obiekt. Także tak jak w tym przypadku new int; zwróci int*.

1
komentarz 7 sierpnia 2017 przez PoetaKodu Stary wyjadacz (10,990 p.)

skoro utworzyłem w ten sposób nową zmienną to czemu przypisanie pod spodem mi nie zadziała ?

wsk = 12; //nie działa

No cóż, dla mnie to jednoznacznie wskazuje na to, że pyta się dlaczego musi pisać:

*wsk = 12; a nie może wsk = 12;

skoro jest to nowa zmienna. Poczekamy i zobaczymy co autor odpisze, w każdym razie dobrze że otrzymał obydwie odpowiedzi.

2
komentarz 7 sierpnia 2017 przez criss Mędrzec (172,590 p.)
Cokolwiek by to nie było (bo faktycznie jest troche dwuznacznie), wolałem napisać :)
komentarz 7 sierpnia 2017 przez Jakub 0 Pasjonat (23,120 p.)

no dobra,powiedzmy że zrozumiałem . Myślałem wcześniej że taki zapis przypomina ten : int *arr = new int[10]; i że tym razem zamiast tablicy tworzymy nowego inta ,no ale się myliłem ... Pozostaje mi tylko pytanie czemu ten sposób działa gdy mamy wskaźnik na jakiś obiekt . np:

struct liczba //jakas struktura
{
int wartosc;
liczba *next;
 
};
 
void dodaj (liczba **root)
{  
liczba *nowa=new liczba; //ten zapis
cin>>nowa->wartosc; //traktujemt to noramalnie jak zmienna (obiekt dokładnie) i nie uzwywamy *
nowa->next=*root;
*root=nowa;
}

to jest przykład stosu i tu jest to możliwe

2
komentarz 7 sierpnia 2017 przez criss Mędrzec (172,590 p.)
edycja 7 sierpnia 2017 przez criss

Tutaj operator -> zastępuje ci gwiazdke. To tak samo jakbyś napisał
*(nowa).wartosc.

Podobne pytania

+1 głos
1 odpowiedź 456 wizyt
pytanie zadane 24 września 2018 w C i C++ przez k4to Początkujący (310 p.)
0 głosów
4 odpowiedzi 4,458 wizyt
pytanie zadane 4 kwietnia 2016 w C i C++ przez esp0x309 Użytkownik (510 p.)
+2 głosów
2 odpowiedzi 423 wizyt
pytanie zadane 8 sierpnia 2017 w C i C++ przez Huberti Gaduła (4,500 p.)

92,556 zapytań

141,403 odpowiedzi

319,559 komentarzy

61,942 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

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy 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!

...