Witam
wie ktoś może jak działa wskaznik na wskaznik? Bo na AVR jest to często używane. Bo jak mam taką funkcję np.
Kod: C / C++
Zaloguj się, aby zobaczyć kod
No to wiadomo te ** to jest wskaźnik na wskaźnik ale czy nie można byłoby zrobić tak:
Kod: C / C++
Zaloguj się, aby zobaczyć kod
zrobimy coś takiego że przekazujemy adres zmiennej np &a, i teraz jak chcę znać adres od której zaczyna się alokacja to piszę *a. Tylko trzeba by było zamiast char napisać pewnie int(bo adres będzie 16bit). Można tak?
Ale kurczę no jakoś nie jestem wstanie tego wskaznika na wskaznik wyobrazić.
W tym drugim przypadku procedura dostaje jako argument wartość adresu. *ret - to bajt wskazywany przez ten adres. Temu bajtowi (znakowi) próbujesz nadać wartość równą adresowi - nie zmieśżci się i nie ma to żadnego sensu.
W pierwszym przypadku dostajesz jako argument adres, pod którym zapisany jest adres, i pod ten adres zapisujesz swój adres - to ma jakiś sens.
Moim prywatnym zdaniem jednak używanie dynamicznej alokacji pamięci w uC, który tej pamięci ma pojedyncze KiB, jest pozbawione jakiegokolwiek sensu i jest proszeniem się o błędy.
No i co przypiszesz wtedy do wartości char wskaznik?
Tak jak myślisz nie można.
char * to wskaznik na obiekt typu char
char ** to wskaznik na wskaznik do obiektu typu char
Aby ci uzmysłowic to ztobię inaczej
char c = 'a'
char *ptr_to_c = &c; // wskaznik do zmiennej c
char **ptr_to_ptr_to_c = &ptr_to_c; // to wskaznik wskazujacy na ptr_to_c.
Teraz dereferencje.
aby "dostać się" do zawartości zmiennej c to musze zrobić *ptr_to_c
aby "dostać się" do zawartości tego wskaznika muszę zrobić *ptr_to_ptr_to_c a następnie do zawartości c *(*ptr_to_ptr_to_c) a w skrócie **ptr_to_ptr_to_c - nawiasy tylko do zobrazowania.
Tak, dobrze to pokazałeś. Najłatwiej sobie rozkminiać takie problemy w symulatorze. Masz na wszystko podgląd.
Dodano po 3 [minuty]:
BlueDraco napisał:
Moim prywatnym zdaniem jednak używanie dynamicznej alokacji pamięci w uC, który tej pamięci ma pojedyncze KiB, jest pozbawione jakiegokolwiek sensu i jest proszeniem się o błędy.
A jakie znaczenie ma ilość dostępnej pamięci? Jeśli masz 1 MB, a będziesz alokował bloki po kilkaset kB, to uważasz, że sens to będzie miało?
Owszem, używając alokacji dynamicznej trzeba uważać, z pewnością nie należy jej nadużywać. Niemniej pisząc, że alokacja dynamiczna na MCU mającym parę kB RAM jest pozbawiona sensu, w praktyce piszesz, że używanie C++ w takich warunkach jest pozbawione sensu, podobnie jak wielu technik programowania - np. dynamicznych list. Z tym trudno się zgodzić.
Czasami po pewnym czasie wracam do niektórych zagadnień stąd ta przerwa.
Równie dobrze można byłoby napisać z jedną gwiazdką i do takiego argumentu możemy przekazać adres wskaźnika. Potem pod ten adres wyłuskany czyli adres wskaznika wpisujemy adres zalokowanej pamięci. Wydaje mi się że można by tak było.
*ret jest typu char - ten błąd został już wytłumaczony wyżej. Do 8-bitowej zmiennej typu char próbujesz zapisać wskaźnik p, kt
ry ma więcej niż 8 bitów. Cały pomysł jest mało sensowny (przyczyny też wytłumaczone), ale jeśli już, to argument powinien być typu char **ret.
Do 8-bitowej zmiennej typu char próbujesz zapisać wskaźnik p,
nie do końca jak bym przekazał do funkcji adres wskaźnika
char *wsk;
i przekazany argument &wsk;
tzn. że po wyłuskaniu ten adres(zalokowanej pamięci) zapisuję do wskaźnika a nie do zmiennej typu char wskaźniki są 16 bitowe. Przecież taki wskaźnik on chce tylko adres co mi szkodzi przekazać adres innego wskaźnika
i przekazany argument &wsk;
tzn. że po wyłuskaniu ten adres(zalokowanej pamięci) zapisuję do wskaźnika a nie do zmiennej typu char wskaźniki są 16 bitowe. Przecież taki wskaźnik on chce tylko adres co mi szkodzi przekazać adres innego wskaźnika
Coś Ci się mocno polątało. ret jest lokalny i to co zapiszesz ret = costam zniknie po powrocie z funkcji. jezeli chcesz modyfikować sam wskaznik to musi to byc **
No wiadomo że ret jest lokalny ja przekazuje np. adres wskaźnika tj. powiedzmy 0x03 funkcja wyłuskuje mi to co pod tym adresem jest to jest sam wskaźnik i tam zapisuje adres więc zmienni mi globalny wskaźnik tj. zapisze do niego adres zalokowanej pamięci ale czytając twój post to nie bardzo wiem o co ci chodzi ret=costam przecież w kodzie jasno jest pokazane *ret.
No bo słuchajcie patrząc tak z logicznego punktu widzenia to według mnie w ogóle nie powinno definiować się wskaźników na wskaźnik tak.
char **wsk;
przecież mogę zdefiniować sobie wskaźnik tak
char *wsk i do niego przekazywać adres innego wskaźnika np. przykład który mi podałeś.
Kod: C / C++
Zaloguj się, aby zobaczyć kod
nie widzę różnicy w takim zapisie
Kod: C / C++
Zaloguj się, aby zobaczyć kod
Po prostu do wskaźnika wrzucam adres innego wskaźnika. Według mnie ale mówię to jest moje zdanie i nie chcę tutaj się wymądrzać ani poprawiać twórców C używanie ** ma sens jak chcę się dostać do tego na co wskazuje ten drugi wskaźnik (Bo pierwszy wskazuje na drugi wskaźnik a ten drugi na zmienną w przypadku który był poniżej).
Np. jakbym chciał stworzyć łańcuszek tj. wskaźnik na wskaźnik na ... to
char *wsk1;
char *wsk2;
char *wsk3;
char *a;
wsk1=&wsk2;
wsk2=&wsk3
wsk3=&a;
1-adres 2 wsk
2-adres 3 wsk
3-adres zmiennej a
jakbym chciał wyłuskać a to napisałbym tak ***wsk1. No to to ma sens (tak mi się wydaje) a idąc tokiem Piotrus_999 to musiałbym pisać ***wsk1; żeby zdefiniować wskaźnik a w moim przypadku to jest tylko 1 gwiazdka można tak?
Zrozum że ta dyskusja jest bezcelowa jeśli my piszemy o języku C, a Ty o "mój prywatny i fajniejszy wariant C który sobie właśnie wymyśliłem".
squelch napisał:
Według mnie ale mówię to jest moje zdanie i nie chcę tutaj się wymądrzać ani poprawiać twórców C używanie ** ma sens jak chcę się dostać do tego na co wskazuje ten drugi wskaźnik (Bo pierwszy wskazuje na drugi wskaźnik a ten drugi na zmienną w przypadku który był poniżej).
A masz jakąś motywację tych wszystkich dziwnych pomysłów, czy po prostu na klawiaturze zepsuł Ci się symbol '*' że tak kombinujesz?
squelch napisał:
No to to ma sens (tak mi się wydaje)
Zgadza się - wydaje Ci się. C i C++ (przy czym C++ bardziej) są jezykami "strongly typed" na etapie kompilacji, a to co sugerujesz oznaczałoby, że kompilator już by nie wiedział czy chodzi Ci o "char" (1 bajt), czy może o "char *" (więcej niż 1 bajt).
Do "char" można przypisać "znak", a odczytać "znak".
Do "char *" można przypisać "adres zmiennej w której jest znak", a nie "adres w którym jest coś". Odczytać można ten adres, lub - przez pojedynczą dereferencję - znak który jest pod tym adresem.
Do "char **" można przypisać "adres zmiennej w której jest adres zmiennej w której jest znak". Odczytujesz "adres zmiennej w której jest adres zmiennej w której jest znak", po pojedynczej dereferencji - "adres zmiennej w której jest znak", lub po podwójnej dereferencji "znak".
itd.
Z tego względu jak do zmiennej "x" typu "char *" przypiszesz "adres zmiennej w której jest adres zmiennej w której jest znak" to akurat kompilator tylko ostrzeże Cię, że robisz coś bardzo głupiego, niemniej jednak próba odczytania "**x" skończy się błędem kompilacji. Związane jest to z tym, że wyrażenie to kompilator rozpatruje jako "*(*x)" (jeśli i to kwestionujesz, to szukasz informacji o tym jaka jest łączność operatorów i ich priorytet). To co jest wewnątrz nawiasów zwróci "znak", np. literkę 'A', typu "char". To co jest poza nawiasem jest błędem kompilacji, ponieważ nie jest możliwa "dereferencja znaku" - operacja "*'A'" nie ma żadnego sensu.
Tylko i wyłącznie tyle. Nie musisz tego przyjmować na wiarę ani uważać że "tak jest bo pewnie ludzie na forum sobie tak założyli". Taki jest standard tego języka i koniec - jeśli nie wierzysz, to po prostu spróbuj skompilować swój kod i przekonasz się, że kompilator też ma inne zdanie niż Ty. O ile w przypadku przypisania adresów kompilator obdzieli Cię tylko warningiem (kompilator C++ zgłosiłby błąd - jedna z głównych zalet tego języka), o tyle próby odczytu które proponowałeś są niemożliwe do wykonania bez rzutowania, ponieważ w każdym innym przypadku spowodują błąd.
Dokładnie tak samo jest w przypadku innych typów - np. w zmiennej typu float nie można przechowywać wskaźnika, a w zmiennej typu int nie można przechowywać liczby z ułamkiem. To że akurat operacja z drugiego przykładu nie powodują błędów jest związane z tym, że w standardzie języka zdefiniowane są pewne "konwersje domyślne" (szukasz pod hasłem "implicit conversions"), które w takiej sytuacji, we w pełni zdefiniowany sposób, skonwertują liczbę z ułamkiem na liczbę całkowitą (obcinając część ułamkową). Takie domyślne konwersje nie istnieją między typem "wskaźnik" a "wskaźnik na wskaźnik", a więc są albo "undefined behaviour", albo powodują błąd kompilacji.
Możesz się z tym nie zgadzać lub nie, niemniej jednak próby kłócenia się i przekonywania nas są bezcelowe. Jeśli te cechy języka C czy C++ Ci nie odpowiadają, to zacznij używać Pythona lub jakiegoś innego języka w którym określenie typu zmiennej nie jest potrzebne.
... w praktyce piszesz, że używanie C++ w takich warunkach jest pozbawione sensu, podobnie jak wielu technik programowania - np. dynamicznych list. Z tym trudno się zgodzić.
W pełni C++ ma sens, choć bez / z ostrożnością co do biblioteki standardowej.
A jakbym nie miał spać, myśląc czy użyłem właściwej ilości gwiazdek (żart) albo czy inkrementuję to, co zamierzałem (dla mnie poważnie), to moim sposobem jest referencja na wskaźnik. Jest to w obrębie C++, ale ani nikogo chyba nie rzuca na kolana.
Kod: C / C++
Zaloguj się, aby zobaczyć kod
PS. OK, to takie C z klasami, nie będę się kłócił.
PPS: kol @squelch , twoje gdybania zamiast poczytania są błędne.