Jakie jest dobre wytłumaczenie dla wskaźników? [Zamknięte]

72

Czy podczas własnych studiów (na własną rękę lub na zajęcia) miałeś moment „ah ha”, kiedy w końcu naprawdę zrozumiałeś wskazówki? Czy masz jakieś wyjaśnienie dla początkujących programistów, które wydaje się szczególnie skuteczne?

Na przykład, kiedy początkujący po raz pierwszy spotykają wskaźniki w C, mogą po prostu dodawać &s i *s, aż się skompiluje (tak jak ja kiedyś to zrobiłem). Może to obraz lub naprawdę dobrze zmotywowany przykład sprawił, że wskaźniki „kliknęły” dla ciebie lub twojego ucznia. Co to było, a co próbowałeś wcześniej, to nie działało? Czy były jakieś wymagania wstępne (np. Struktury lub tablice)?

Innymi słowy, co było konieczne, aby zrozumieć znaczenie &si, a *kiedy można je było używać z pewnością? Nauka składni i terminologii lub przypadków użycia nie wystarczy, w pewnym momencie pomysł musi zostać zinternalizowany.


Aktualizacja: Jak dotąd bardzo lubię odpowiedzi; proszę, niech nadchodzą. Jest tu wiele wspaniałych perspektyw, ale myślę, że wiele z nich to dobre wyjaśnienia / hasła po zinternalizowaniu tej koncepcji. Szukam szczegółowych kontekstów i okoliczności, kiedy do ciebie dotarło.

Na przykład:

Jedynie w pewnym stopniu zrozumiałem wskaźniki składniowo w C. Słyszałem, jak dwóch moich przyjaciół wyjaśnia wskaźniki innym przyjacielowi, który zapytał, dlaczego a structprzekazano ze wskaźnikiem. Pierwszy znajomy mówił o tym, jak należy się do niego odwoływać i modyfikować, ale był to tylko krótki komentarz innego znajomego, w którym mnie uderzył: „Jest także bardziej wydajny”. Przekazywanie 4 bajtów zamiast 16 bajtów było ostatnią potrzebną zmianą pojęciową.

Macneil
źródło
16
Metoda „strzelby”: rzucaj * wszędzie, dopóki nie zadziała.
Michael K
15
@Michael: Tak, powodzenia z tym.
Robert Harvey,
8
To pytanie pojawia się wkrótce po rozpoczęciu każdego semestru na SO. Najnowsze wcielenie jest tutaj: stackoverflow.com/questions/4118647/...
Tim Post
10
Jakie jest dobre wytłumaczenie dla wskaźników? Brian Kernhigan i Dennis Ritchie nienawidzą inżynierów oprogramowania.
Adam Crossland,
3
„Kliknął” dla mnie po tym, jak spędziłem trochę czasu w maszynie do debugowania. Tak, znałem podstawy architektury komputerowej - nauczyłem się montażu na Commodore 64, a później x86 ASM był dość prosty. Więc znałem już koncepcję „adresu”, po prostu nie rozumiałem, w jaki sposób mapuje się na konstrukcje składniowe C.
zvrba

Odpowiedzi:

25

Schemat Memory-as-a-Grid

Zwykle to, co robię, to reprezentowanie pamięci jako „siatki”, dzięki czemu mogę tworzyć adresy, wyróżniać różne przestrzenie pamięci i zapisywać wartości komórek (a nawet ich reprezentacje binarne) i łączyć wskaźniki w pamięci z wartościami, które wskazywać na. (A potem nadal wspominam, że to uproszczenie).

Zwykle dla większości moich uczniów jest to moment „och”.

Symbol Żonglerka

Jeśli chodzi o to, aby przestali zapominać o użyciu & i *, jest to bardzo proste: przedstaw to w ten sam sposób, w jaki wykonują obliczenia matematyczne lub fizyczne. Jeśli podzielisz odległość w km na godzinę w ciągu godziny, uzyskasz prędkość w km / h. To, co jest w środku, musi być na zewnątrz. Prosty.

printf na ratunek

Wykonanie zaledwie kilku podstawowych przykładów, które wizualnie przedstawiają to, co wyjaśniłeś, uspokoi ich w tym, co według nich zrozumieli, lub da im możliwość powiedzenia „ah, nie rozumiem tego”.

Bądź obszerny

Wskaż wskaźniki dla prostych typów i upewnij się, że rozumieją różnicę między adresowaniem a rozmiarem typu danych, a następnie struktury, następnie tablice i wiele poziomów.

Następnie zacznij arytmetykę wskaźnika.


Dodatek: Rekurencja

Zazwyczaj tłumaczę rekurencję podobnie, używając reprezentacji wizualnej. Poproś, aby wydrukowali alfabet za pomocą gotowej funkcji, która zapisuje pojedynczy znak, a następnie poproś, aby wydrukowali go w odwrotnej kolejności, po prostu zmieniając dwie linie.

Zwykle jest „co ...?” chwila, a kiedy dodasz kolejny parametr do printf, aby wydrukować wartości liczbowe i wciąć kroki, staje się to ulgą.


Alternatywy: model Play-Doh i kubki z wodą

Miałem kilku współpracowników na uniwersytecie, którzy pokazali studentom wideo wyjaśniające wskazówki i dostęp do pamięci za pomocą pasty play-doh. Było to niezwykle sprytne i dobrze zrobione, chociaż nigdy tak naprawdę nie korzystałem z tej techniki, z wyjątkiem bardzo młodych uczniów zainteresowanych chwytaniem programowania (ale zwykle takich, których nie prowadziłbym do języka używającego wskaźników zbyt wcześnie). Zasadniczo za pomocą małych kulek play-doh, które można dołączyć do innych większych kulek play-doh reprezentujących przestrzenie pamięci, i które można łączyć ze sobą, aby je połączyć (jak w połączonej strukturze danych) lub scalić (jak w ciągłym pamięć). Używanie różnych kolorów dla wskazanych obszarów pamięci i wskaźników również pomaga. Ale nadal uważam, że funkcja Memory-as-a-Grid działa lepiej, jak można wyraźnie pokazać, że wskazywanie jest tak naprawdę kwestią „adresowania”, jak na „mapie / siatce”. Podczas gdy tryb Play-doh nadal myli ich w myśleniu, że rzeczy naprawdę „dotykają się” w pamięci.

Z kubka z wodą korzystała również bezpośrednio koleżanka, ale nie wiem, czy ona to wymyśliła. To było ciekawe podejście, ale zauważyłem, że wielu uczniów nie rozumie wyjaśnienia. Coś podobnego do techniki filiżanki kawy DevSolo . Ale myślę, że jest to w rzeczywistości wprowadzające w błąd, ponieważ powoduje, że uczniowie mylą pojemniki, struktury danych, wskaźniki i tablice. Zakładam, że może to być interesujące podejście do wyjaśniania tablic na początku, ale nie trzymałbym się tego zbyt długo.

szaleństwa
źródło
Wow, dzięki za nagrodę. Cieszę się, że odpowiedź została doceniona.
haylem
Wyciągnij to: | to brzmi jak dobre nauczanie!
Spooks
80

Ktoś dużo mądrzejszy niż kiedyś powiedziałem:

Zakonnica Wu Jincang zapytała Szóstego Patriacha Huinenga: „Studiowałem sutrę Mahaparinirwany od wielu lat, ale jest wiele obszarów, których nie do końca rozumiem. Proszę, oświeć mnie”.

Patriarcha odpowiedział: „Jestem niepiśmienny. Proszę, przeczytajcie mi postacie, a być może uda mi się wyjaśnić znaczenie”.

Zakonnica powiedziała: „Nie możesz nawet rozpoznać postaci. Jak więc rozumiesz znaczenie?”

„Prawda nie ma nic wspólnego ze słowami. Prawdę można przyrównać do jasnego księżyca na niebie. W tym przypadku słowa można przyrównać do palca. Palec może wskazywać lokalizację księżyca. Jednak palec nie jest księżyc. Aby spojrzeć na księżyc, trzeba patrzeć poza palec, prawda? "

Frank Shearar
źródło
13
+1 za odsunięcie księżyca. +1 za koana, gdybym mógł.
Tim Post
14
Jaki jest dźwięk głosowania jednym palcem?
Adam Crossland,
3
@Adam, który zależy od tego, czy używasz myszy, czy gładzika.
Daniel Joseph,
7
@Frank To był WIELKI cytat. Nadal uważam, że nie pomoże to zrozumieć tego pomysłu. Specjalnie dla kogoś, kto nie zna wskaźników.
Gulshan,
11
To piękna historia ... ale podejrzewam, że ludzie głosują z tego powodu, a nie dlatego, że dobrze wyjaśniają wskazówki.
Kyralessa
46

Przekonałem się, że diagramy były bardzo pomocne. Przykład:

schemat wskaźnika


Ten rodzaj diagramu pomógł mi zobaczyć, że wskaźniki były ich własną zmienną, ale zawierały wartość, która była lokalizacją innego obiektu, tj. Tablicy lub łańcucha. Ponadto, kiedy wykonano ołówkiem, mogłem go użyć do śledzenia mojego programu na papierze lub na tablicy / tablicy.

Michael K.
źródło
4
Albo wartość w „wskaźniku” powinna wynosić 4, albo wskaźniki tablicy (lub adresy pamięci) powinny zaczynać się od 0. W przeciwnym razie, w jaki sposób wskaźnik o wartości 3 może wskazywać na lokalizację 4?
MAK
++ Dokładnie tak zamierzałem to wyjaśnić. Zaczynałem w Fortran (na dobre lub złe!). Dawno temu w Fortranie używaliśmy tablic równoległych zamiast tablic struktur (nie ma czegoś takiego jak nowy ). Jeśli jedna tablica zawierała indeks „innego wiersza” w tablicach, nazwaliśmy ją „wskaźnikiem”, co było ekscytujące. Zrobiliśmy powiązane listy, drzewa, ty to nazywasz.
Mike Dunlavey,
2
Znalazłem wskaźniki najlepiej wyjaśnione za pomocą zdjęć wskazujących, gdzie są przechowywane, oraz strzałek wskazujących, gdzie wskazują wskaźniki.
gablin
32

Kiedy po raz pierwszy „dowiedziałem się” o wskaźnikach, byłem w pewnym sensie w to zaangażowany. Moja uczelnia podjęła decyzję na długo przed zapisaniem się do centrowania programu nauczania w Javie, więc kiedy mój profesor struktur danych wygłosił jeden wykład na temat C i poprosił nas o zaimplementowanie listy XOR ze wskazówkami, czułem, że zaczynam coś robić sposób nad moją głową.

Zrozumiałem definicję:

Wskaźnik to zmienna zawierająca adres zmiennej

Ale nadal nie rozumiałem dużej części tej koncepcji. Patrząc wstecz, myślę, że koncentrowało się na trzech rzeczach:

  1. Czym dokładnie jest lokalizacja pamięci? (W tym czasie nie brałem udziału w zajęciach z organizacji komputerowej)

  2. Niezręczna składnia (Więc um ... dlaczego dokładnie jest zdefiniowana jako „int * ip”, ale następnie odnoszę się do zmiennej jako „ip”?)

  3. Jak dokładnie warto przechowywać adres zmiennej, a nie tylko jej używać?

Dopiero po zakupie książki K&R i rozwiązaniu każdego problemu naprawdę zrozumiałem wskazówki. Oprócz tego, że od dawna ukończyłem klasę organizacji komputerowej (która moim zdaniem powinna być wymagana przed nauczeniem się języka C), częściowo miało to związek z faktem, że zdałem sobie sprawę, że wskaźników można używać do funkcji, tablic, struktur ... do robienia użytecznych rzeczy, a nie tylko do przechowywania adresów zwykłych zmiennych.

Ale moim momentem „aha” był sposób, w jaki K&R wyjaśnił niezręczną składnię definiowania prostego wskaźnika. W całej książce robiłem notatki (w których przeformułowałem uwagi zawarte w książce w moich własnych słowach, aby lepiej zrozumieć), i to jest ta odnosząca się do tego:

Wskaźnik to zmienna zawierająca adres zmiennej. Wskaźnik jest zarówno zdefiniowany, jak i wyodrębniony (uzyskując wartość przechowywaną w miejscu pamięci, do którego wskazuje) za pomocą operatora „*”; wyrażenie jest mnemoniczne.

Ex.: 
   int a;      /* Variable 'a' is an integer */

   int *ip;   /* Variable ip is a pointer and dereferencing it gives an integer.
                 In other words, the expression *ip is an int, so ip is a pointer
                 to an int */

Zawsze czułem, że nie jestem w stanie w pełni zrozumieć bardziej zaawansowanych koncepcji wskaźników, dopóki nie mam w głowie czegoś tak podstawowego. Niepokoiło mnie to w nieskończoność po tym zadaniu (przy okazji, którego nie zrobiłem zbyt dobrze;)), dlaczego „* ip” nie istniało zaraz po tym, jak (myślałem, że) zdefiniowałem „* ip”. Opanowanie tego jest niezbędne w bardziej zaawansowanych koncepcjach obejmujących wskaźniki, takie jak wskaźniki funkcji i bardziej skomplikowane definicje, takie jak to:

char (*(x())[])()

Podsumowując, myślę, że koncepcja wskaźników wymaga:

  1. Podstawowe zrozumienie tego, jak pamięć jest układana w komputerze (i czym jest pamięć).

  2. Wiedza o tym, jak potężne mogą być wskaźniki (wykorzystanie w świecie rzeczywistym, a nie tylko abstrakcyjna koncepcja, której uczą się w celu uczenia się).

  3. Rozszyfrowanie hieroglifów zwanych potocznie „definicją wskaźnika”

Podsumowując, myślę, że powinny być co najmniej 3 momenty „aha” podczas nauki o wskaźnikach. Nadal jestem studentem, więc pomyślałem, że docenisz punkt widzenia kogoś, kto wciąż (względnie) nauczył się tej koncepcji.

Kevin
źródło
+1 za 3 „Ah ha!” pieniądze Wskaźniki Groking są w warstwach. „Rozumiałem” punkty przez długi czas, zanim zdałem sobie sprawę, że tylko około połowa zrozumiałem wskaźniki do wskaźników :)
Binary Worrier
Myślę, że masz całkowitą rację, ponieważ bardzo duża część „niezrozumienia wskaźników” potknęła się o dziwną składnię C. [Myśli: Zastanawiam się, jak trudno byłoby grokować z tego rodzaju składnią: PointerTo<int> a]
Benjol,
Zgoda, @Benjol. Dziwi mnie, że składnia int* ipnie jest częstsza. W końcu zacząłem rozumieć wskaźniki, kiedy zobaczyłem kod sformatowany w ten sposób. Następnie mógłbym odczytać to jako „ip jest typu int pointer”.
Jakub
19

Wskaźnik przypomina trochę skróty do aplikacji na pulpicie. Usuń skrót, a cel nadal istnieje. Uruchom skrót, a cel zacznie działać.

Zawsze wyjaśniam, jak to działa, po prostu tworząc plik txt na pulpicie i dwa skróty do pliku. Po skopiowaniu i usunięciu skrótów widać, że ludzie rozumieją ideę „referencji”.

Gdy grupa zrozumie podstawowe informacje o skrótach, możesz zacząć wyjaśniać wskaźniki w dowolny sposób. Prawdopodobnie zrozumieją to dość łatwo.

Barfieldmv
źródło
To nie jest zła analogia. Tylko kilka punktów, które należy dodać dla każdego, kto bierze to dosłownie „hard-link” zawsze wskazuje na cel, nawet jeśli zmieni katalogi, wskaźnik wskaże oryginalną pamięć, nawet jeśli obiekt zostanie przeniesiony. „Hard-link” powie ci, kiedy cel już nie istnieje, zwisający wskaźnik powinien być ustawiony na NULL.
snmcdonald
Twardy link przypomina inteligentny
licznik
1
+1. Jedna z najlepszych praktycznych analogii do wyjaśnienia wskaźników. Jestem pewien, że uczniowie natychmiast wpadną na ten pomysł, ponieważ ta funkcja jest najczęściej używana przez wszystkich.
Karthik Sreenivasan,
13

8-10 lat temu prowadziłem kurs wprowadzający „C” na uniwersytecie. To zawsze był fajny temat do odkrycia. To, co wydawało się działać najlepiej, a było to po kilku rozmowach z współpracownikiem, polegało na użyciu filiżanki kawy i dłoni.

Użyłem analogii filiżanki kawy (lub ich rzędu do tablic) jako zmiennej (może coś pomieścić). Następnie użyłem dłoni, która również mogła coś trzymać lub, przesuwając palec wskazujący, aby „wskazać” filiżankę kawy.

Ścisła ręka była zerowa, palec wskazujący na moją głowę (jak fałszywy pistolet) był zwisającym wskaźnikiem.

Następnie, po kilku demonstracjach i podróżach przez debugger, kliknął większość.

DevSolo
źródło
3
Już mnie pomyliłeś.
kirk.burleson
1
@Kirk, jak to? pozwól mi zobaczyć, czy mogę to wyjaśnić.
DevSolo,
4
++ Nie ma nic takiego jak nauczanie, aby skłonić cię do myślenia o tych rzeczach.
Mike Dunlavey,
Jeden z moich wykładowców na uniwersytecie zrobił coś podobnego, wykorzystując studentów (jednym z nich był „Niel”, dla dodatkowego śmiechu), zamiast filiżanki kawy. Machanie ręką na publiczność było wiszącym wskaźnikiem, ponieważ mogło wskazywać na wszystko.
Kaz Dragon
2
Wiszący wskaźnik bardziej przypomina wskazywanie na Neila, niż zmuszanie go do opuszczenia krzesła bez zmiany miejsca, w którym wskazujesz ... a następnie próbowania podążania za tym wskaźnikiem i interpretowania tego, co tam znajdziesz, jako Neila, niezależnie od tego, kto tam trafił.
Jon Purdy,
10

Naprawdę bardzo się martwię, gdy słyszę pytanie „jak zrozumiałeś wskazówki”. Zawsze myślałem, że ta koncepcja jest niesamowicie prosta, a logiczna ewolucja języków zapewnia wielką moc programistom.

Martwi mnie to, że nigdy nie miałem trudności ze zrozumieniem pojęcia wskaźników. Więc kiedy jesteś tutaj „kiedy w końcu go dostałeś”, zaczynasz myśleć:

Czy faktycznie to rozumiem? Może nigdy tego nie zrobiłem?

Być może powodem, dla którego ta koncepcja wydaje się trudna, jest to, że ciągle mówimy wszystkim, którzy jeszcze się z nią nie spotkali, że wskaźniki są tak trudne, a oto sto sposobów na naukę?

Po prostu rzucam ten pomysł, oczywiście osobiście uwielbiam dobry schemat, a Steve Gibson zawsze fantastycznie tłumaczy wszystko !

Marcus Whybrow
źródło
Ciekawe ... jakiego języka używałeś w tym czasie? Wydaje mi się, że składnia C wywołuje wiele problemów. Gdybyś znał najpierw zgromadzenie, widziałbym, że to jeden z powodów. [BTW, chcę to zagłosować, ale dzisiaj osiągnąłem limit.]
Macneil
Co ciekawe, przez długi czas nie znałem „niebezpiecznych” wskaźników, pracując w Pythonie (i Javie na uniwersytecie). Potem wziąłem moduł „metod zorientowanych obiektowo”, który używał C ++ jako wybranego języka i zagłębiłem się w to.
Marcus Whybrow
Myślę też, że zrozumienie tego, jak program jest reprezentowany w pamięci może zdecydowanie pomóc, ale myślę, że wolę powiedzieć, że zrozumienie, dlaczego wskaźniki były użytecznym dodatkiem do języka, jest najskuteczniejszym kątem, ponieważ przechodzisz to samo proces jako oryginalni projektanci.
Marcus Whybrow
+1 Nie mam pojęcia, dlaczego ludzie uważają wskaźniki za trudne lub tajemnicze.
ggambett 12.03.11
Tak, wskaźniki zawsze były tak oczywiste proste i użyteczne, że nie wyobrażam sobie, aby myśleć, że jest w nich coś „trudnego”. Nienawidzę tego, gdy nauczyciel jakiegokolwiek kursu mówi „teraz, to jest trochę trudne”, ponieważ jest tak wywrotowy. Naucz tych cholernych rzeczy i pozwól uczniom sami zdecydować, czy uważają to za trudne.
Jon Purdy,
7

Nigdy nie miałem większego problemu ze wskaźnikami w C, ale może to być spowodowane tym, że najpierw musiałem nauczyć się asemblera. To była ulga, że ​​nie musiałem już samodzielnie zarządzać adresami. Więc może odpowiedzią (zakładając, że tego uczysz) jest zapewnienie uczniom emulowanego języka asemblera do pracy. Oni będą ustalić to w trakcie pisania czegoś bardziej skomplikowanego niż „Hello, World”.

Larry Coleman
źródło
+1 za dostosowanie bardziej prymitywnej abstrakcji zamiast próby użycia analogii.
Anonimowy typ
W moim liceum jedna jednostka informatyki była poświęcona badaniu komputera zapałkowego , którego można uczyć z tablicy bez dotykania lub angażowania prawdziwego komputera.
rwong
@ Larry Coleman, Zgromadzenie !!!!!!!!!!! Chcę spróbować najpierw przed C. Powiedz mi, od czego zacząć naukę. Darmowe ebooki, bezpłatny PDF? IDE? Proszę!!!!!!!!!!!!!!!!!!!
Spacez Ly Wang
7

Wskaźnik to zmienna, której wartością jest adres pamięci innej zmiennej.

kirk.burleson
źródło
Elegancki, dlaczego to bardziej skomplikowane.
Bjarke Freund-Hansen
Jeśli wskaźniki nie wydają się od razu intuicyjne i przydatne, zostały źle wyjaśnione.
Jon Purdy,
6

Jak naprawdę dowiedziałem się o wskaźnikach? Pisząc prosty kompilator student pierwszego roku studiów.

Jak wyjaśnić wskaźniki w terminach laika? Podoba mi się (datowana?) Analogia katalogu bibliotecznego przechowywanego na kartach indeksowych. Każda karta („wskaźnik”) zawiera informacje o tym, gdzie znajduje się jakaś książka („dane”), ale tak naprawdę nie zawiera samej książki. Jeśli zmodyfikujesz kartę („arytmetyka wskaźnika”), wystarczy zmienić książkę, na którą wskazuje, i nie ma wpływu na samą książkę - uważaj, aby nie zepsuć adresu lub możesz wskazać nieistniejącą książkę lub nawet zła biblioteka. Jeśli jednak podążysz za „adresem” na karcie i przejdziesz do odpowiedniej części biblioteki („odłóż wskaźnik”), możesz zobaczyć / zmodyfikować samą książkę.

Jewgienij Brikman
źródło
+1 za pamięć o moim kursie CS, gdzie miałem dokładnie to wyjaśnienie
Gary Rowe
To w pewnym sensie dobre, ale nieco mylące, ponieważ karty tak naprawdę nie mówią, gdzie jest książka. Nadal musisz wykonać algorytm wyszukiwania zorientowany na tenisówki. Może to lepiej modeluje bazę danych - odnosząc się do przechowywanego obiektu za pomocą klucza (numeru wywoławczego).
DarenW
+1 Analogia zachowuje tę samą koncepcję, co wyjaśnioną przez Barfieldmv, ale z bardziej realistycznym podejściem.
Karthik Sreenivasan,
5

Zakładam, że ktoś zamierzający nauczyć się wskaźników wie, jakie są normalne zmienne i jak działają w C. Teraz spróbujmy zdefiniować wskaźniki z niektórymi jego atrybutami

  • Są to również zmienne, ale o różnym charakterze. Załóżmy, że w przestrzeni zmiennej znajdują się dwie warstwy. Zmienne normalne różnych typów znajdują się w górnej warstwie, a wskaźniki w dolnej warstwie. Jak ta figura

    alternatywny tekst

  • Jak sugeruje nazwa „Wskaźnik”, wskaźniki mogą POINT do czegoś. Tak jak nasz palec może wskazywać jakiś obiekt. Na co wskazują? To są normalne zmienne. W skrócie „Wskaźniki wskazują zmienne normalne”.

  • Podobnie jak normalne zmienne, wskaźniki również mają taką samą liczbę typów, jak int, char lub float. Wskaźnik określonego typu może wskazywać tylko ten sam typ zmiennych.
  • Wskaźnik może wskazywać na jedną zmienną, a później ten sam wskaźnik może wskazywać na inną zmienną. Tylko typ powinien być taki sam. Tak więc powiązanie wskaźnika z jakąś zmienną nie jest trwałe i można je zmienić.
  • Jak więc deklarowany jest wskaźnik? Prawie jak normalne zmienne. Musisz poprzedzić nazwę gwiazdką ( *). Lubić-

    int *pointer;
    
  • W jaki sposób wskaźnik jest powiązany ze zmienną? Używanie &operatora przed zmienną taką jak ta instrukcja

    pointer = &variable;
    
  • Jak używany jest wskaźnik, wskazując na zmienną? Odbywa się to również poprzedzając nazwę gwiazdką ( *). Następnie można go użyć zamiast zmiennej, którą teraz wskazuje-

    *pointer = var1 + var2;
    

    zamiast

    variable = var1 + var2;
    
  • Teraz graj ze wskaźnikami z pewnym kodem. Przyzwyczaj się teraz do tej cechy wskaźników. Do tego momentu mówimy o tym, co robią wskaźniki. Kiedy już sobie z tym poradzisz, zacznij badać, w jaki sposób wskaźniki wskazują na jakąś zmienną i jak reagują, jeśli zastosuje się do nich normalne operacje arytmetyczne. Następnie przejdź do relacji między wskaźnikami i tablicami oraz wskaźników do wskaźników.

To wszystko, co zasugeruję na temat uczenia się wskaźników.

Gulshan
źródło
Chciałem podążać za procesem „najpierw co robi, a potem jak”. Abstrakcja!
Gulshan,
5

Edytowane na poparcie zmienionego wymagania pytania

Moja droga do zrozumienia „pointer” (jeśli dobrze pamiętam) poszła w ten sposób. Miałem pewne proste doświadczenie w programowaniu asemblera od czasu, gdy wciąż bawiłem się BBC Micro, więc miałem pojęcie pamięci jako kilku pudełek (patrz poniżej). Zostało to wzmocnione przez użycie tablic. Jednak wchodziłem w świat C i musiałem radzić sobie z ciągami znaków, co oznaczało wskazówki. W BASIC było to trywialne, w asemblerze nigdy nie musiałem z nimi pracować, a teraz w klasycznym C są to wszystkie wskaźniki i inne rzeczy. Ach, ale mogę wrócić do tablic za pomocą ciągów (zakończonych zerem):

char s[] = "Hello, world!";
printf("%s",s);

Dobrze, to tylko tablica znaków (8 bitów na znak w moim małym świecie) z zerowym znakiem na końcu, aby pokazać, gdzie się kończy. Printf po prostu bierze tę tablicę i przesuwa się po niej, drukując ją. Ale co, jeśli chcę przekazać ten ciąg do funkcji?

void print_str(char* p) {
  printf("%s",p);
}

Co mówi instrukcja, ale o co w tym wszystkim chodzi? Hmm, char * oznacza „wskaźnik do char”. OK ... zgubiłem mnie. Potem przyszło mi do głowy, że char * czyni p równoważne s [0]. OK, mogę tego użyć, ale nadal nie zmieniłem wskaźników. Mam na myśli, jak ustawić niektóre dane za pomocą jednej z tych rzeczy? Znowu do instrukcji ...

char* p = "Hello World!";

Kiedy piszę powyższe, mówię sobie: „zadeklaruj wskaźnik do znaku i ustaw go na tę tablicę znaków”. W jakiś sposób ten wskaźnik eliminuje potrzebę stosowania tablicy. Zgadnij, że to kompilator coś dla mnie robi dla odmiany. Jak mogę zmienić tablicę za pomocą tego wskaźnika? Wiem, że mógłbym użyć wersji tablicowej

 s[2] = 'L'; 

ale jaki jest odpowiednik „pointer speak”? Znowu do tego podręcznika ...

*(p+2) = 'L';

Przypuszczam, że * oznacza po prostu „zawartość adresu pamięci” i (p+2)jest s[2]. Co oznaczało, że wskaźnik p był ... po prostu ... adresem ... bong!

Że jest dźwięk oświecenia. Nagle przekręciłem wskaźniki (to było dawno temu, my, stare zegary, nie „grok” aż do później). To była tylko pośrednia reakcja.


Oryginał:

OK, moja wartość 2c:

Wyobraź sobie, że pamięć to mnóstwo pudełek. Każde pole ma z boku numer (adres). Każde pudełko zawiera numer (zawartość). Możesz pracować z tymi polami na 2 sposoby: zmienne (chcę zawartość pola N), wskaźniki (chcę zawartość pola, cokolwiek jest w polu N ). Wskaźniki są po prostu pośrednią.

I na wielką odpowiedź, która obejmuje wszystko, co kiedykolwiek musisz wiedzieć - przeczytaj to .

Gary Rowe
źródło
++ Tak mniej więcej tak uczyłem przedmiotu.
Mike Dunlavey,
Edytowany artykuł do obsługi wymagań PO.
Gary Rowe,
Z jakiej instrukcji korzystałeś, dodając dodatkowe znaki NUL na końcu swoich ciągów?
Rob Gilliam
@raimesh Bardzo stary (około 1991 r.). Nie pamiętam który.
Gary Rowe,
1991? To nie jest stare jak na podręcznik w języku C - pierwsze wydanie K&R zostało opublikowane w 1978 roku! Zaintrygowało mnie to, że kiedykolwiek istniała instrukcja, która sugerowała, że ​​musisz umieścić \ 0 na końcu stałych łańcucha. (Właściwie, patrząc na to jeszcze raz, chciałeś powiedzieć \ n?)
Rob Gilliam
4

Zdobądź małe drewniane klocki.

dodaj metalowe haczyki na jednym końcu, a metalowe oczy na drugim.

możesz teraz utworzyć listę połączoną w rzeczach, z którymi możesz grać.

Spróbuj wyjaśnić za pomocą tego fizycznego rekwizytu. Często żałowałem, że nie mam tego podczas nauczania wskaźników dla studentów pierwszego roku (studentów pierwszego roku).

Mały metalowy haczyk jest wskaźnikiem, klockiem drewna, na który wskazała rzecz.

ZDECYDUJĘ kogokolwiek, aby nie dostał go po zagraniu klockami.

Tim Williscroft
źródło
4

Ułatwiłem moje życie, kiedy po prostu usunąłem cały puch i zacząłem traktować wskaźnik jak każdą inną zmienną, a nie jakąś magiczną istotę (dawno temu w klasie 11) .. po prostu wiem 3 rzeczy:

  1. Wskaźnik to zmienna przechowująca adres innej zmiennej (lub tylko dowolnego adresu).
  2. * służy do uzyskania wartości w miejscu pamięci, które jest przechowywane w zmiennej wskaźnika.
  3. & operator podaje adres lokalizacji pamięci.

Reszta to cukier składniowy i zdrowy rozsądek. Po prostu napisz kilka prostych programów w języku C (takich jak implementacja biblioteki połączonych list), używając wskaźników, aby się zorientować.

Sridhar Iyer
źródło
2
  1. Wskaźnik można traktować jako uogólnienie indeksu na tablicę.
    • Weź pod uwagę, że duża tablica może zostać pocięta na szereg mniejszych, nie nakładających się na siebie tablic o zmiennej wielkości. Teraz te mniejsze tablice są tym, co zwykle nazywamy tablicą. Większa jest wówczas cała przestrzeń pamięci komputera. Proces odcinania mniejszych tablic nazywa się alokacją pamięci.
  2. Zestaw struktur połączonych ze sobą za pomocą niektórych wskaźników można traktować jako wykres kierunkowy .
    • Każdy wierzchołek jest zmienną, która może posiadać pewną wartość.
    • Niektóre zmienne są wskaźnikami, a każdy wskaźnik może mieć dokładnie jedną krawędź wychodzącą w coś innego.
    • Zmienne, które nie są wskaźnikami, nie będą miały przewagi wychodzącej. Mogą mieć dowolną liczbę przychodzących krawędzi.
rwong
źródło
2

Nie pamiętam dokładnie okoliczności wokół moich wskazówek - aha-moment, ale z mocą wsteczną poprawiłem pamięć wokół mojego zrozumienia tablicy w stylu c. (tzn. arr[3]jest taki sam jak *(arr+3))

Z jakiegoś powodu uważam, że niezwykle pomocne jest wyświetlanie wskaźników jako tablic za każdym razem, gdy zdarzają się sytuacje wskaźnika.

Zaz
źródło
2

Zasadniczo @Gary Rowe wprowadził odpowiedni model. Pamięć jako zestaw pól z adresami (liczbami). Każde pudełko przechowuje wartość (liczbę).

Ideą wskaźnika jest interpretacja wartości w jednym polu jako adresu innego pola. Ta wartość jest używana w odniesieniu do konkretnego pola, dlatego nazywa się to odwołaniem . Zatem dereferencje to proces otwierania okna, do którego się odnosi.

jeśli vjest zmienną (pole), to instrukcja

  • voznacza wartość v, tj. daj mi to, co jest w pudełku
  • *voznacza dereferencję wartości v, tj. podaj mi to, co jest w polu, do którego odnosi się wartośćv
  • &voznacza odniesienie v, tj. podaj adres w polu

Myślę, że nie służy to wprowadzeniu wskaźników jako czegoś zupełnie innego. To było pojęcie trudne do zrozumienia dla mnie, gdy byłem dzieckiem. Zawsze wydawało mi się, że to jakaś mroczna magia, której tak naprawdę nigdy nie rozumiałem, która wymagała wielu specjalnych znaków. Po raz pierwszy zrozumiałem, kiedy napisałem małą grę, używając podwójnej pośredniczości w języku, który nie ma arytmetyki wskaźników. To mnie oświeciło.

Wskaźniki są kwestią interpretacji. Myślę, że wyjaśnienie to znacznie ułatwia. Arytmetyka wskaźników to niezwykle proste i intuicyjne operacje, jeśli pokazano prosty przykład z pamięcią 10 zmiennych.

back2dos
źródło
2

Wyjaśnienie, które naprawdę zrozumiałem, to:

Rozważ siatkę miasta z różnymi domami zbudowanymi na kawałkach ziemi. W dłoni trzymasz kawałek papieru. Na papierze, który napisałeś:


Dom Dawida,

112 Taki i taki ulica.


Kawałek papieru (zmienna wskaźnikowa) zawiera adres wskazujący dom Dawida. Kiedy chcesz powiedzieć znajomemu, aby spojrzał na fajny dom Davida, o wiele łatwiej jest użyć kawałka papieru jako odniesienia do domu niż wysłać faktyczny dwupiętrowy budynek pocztą.

Podobnie jak w przypadku prawdziwych wskaźników, możesz mieć kłopoty, podążając za adresem na kartce papieru. David mógł się poruszyć, a kiedy tam dotrzesz, po prostu znajdziesz wielką dziurę w ziemi. W takim przypadku lepiej byłoby usunąć adres na papierze, gdy David się przeprowadził, lub przynajmniej zmienić go na nowy. Możesz również zauważyć, że idziesz pod ten adres i wpisujesz dom, w którym myślisz, że jest to salon Davida, ale tym razem trafiasz do basenu z nieznajomymi. Ktoś inny wykorzystał miejsce pod adresem, które miałeś na coś zupełnie innego.

Per Wiklander
źródło
2

Jeśli chcesz wyjaśnić wskaźniki, najpierw musisz wyjaśnić pamięć. Zazwyczaj robię to przy użyciu papieru milimetrowego / papieru w kratkę z rzędami i kolumnami. Jeśli „uczeń” rozumie pamięć, może zrozumieć, co to jest adres. Jeśli masz adres, masz wskazówki.

Możesz grać z tą abstrakcją. Na przykład wpisz adres (numer) jednego kwadratu na inny kwadrat. Teraz narysuj strzałkę od kwadratu wskaźnika do kwadratu docelowego. Teraz zastąp wskaźnik (np. Zwiększ go) i dostosuj strzałkę. Wpisz adres w inny kwadrat i pozwól uczniowi narysować strzałkę ...

Następny krok: nadaj niektórym kwadratom (jak wskaźnik) nazwę. Teraz możesz wyjaśnić dereferencje. Otóż ​​to.

EricSchaefer
źródło
2
Przypomina mi „komputer”, którego użyłem, zanim zdążyłem zdobyć taki, który faktycznie używał elektronów - karton „CARDIAC”, jak sądzę, tak się nazywał. Dosłownie, pisząc liczby w polach numerowanych, aby uzyskać liczby z innych pól numerowanych. To było edukacyjne i nigdy nie miałem problemu ze zrozumieniem wskaźników dotyczących prawdziwych mikroprocesorów.
DarenW
2

Być może to tylko ja, ale dobrze sobie radzę z analogiami. Powiedzmy, że masz znajomego (funkcję / klasę) „Foo”, który chce mieć kogoś innego (inną funkcję / klasę), „Bar”, z jakiegoś powodu się z tobą skontaktuje. „Foo” może sprawić, że zobaczysz „Bar”, ale nie jest to wygodne, przenosząc wszystkie te istoty (instancje). Jednak „Foo” może wysłać „Bar” twój numer telefonu (wskaźnik). W ten sposób, bez względu na to, gdzie jesteś, „Foo” wie, jak się z tobą skontaktować bez konieczności znajdowania cię.

Powiedzmy, że „Bar” ma znajomego „Baza”, który również chce się z tobą skontaktować. Ale chronisz swój numer telefonu i nie chcesz, aby każdy miał go, „Baz” może wywoływać „Bar” (telefon jako wskaźnik), który może następnie przekazać połączenie do ciebie (inny wskaźnik). I tak w kółko przyjaciół „Baz” i przyjaciół przyjaciół.

Hugo
źródło
2

Wskaźniki uczynić sposób większy sens, jeśli już studiował asemblera i / lub architektury komputerowej. Myślę, że jeśli nauczę klasy C, zacznę od kilku tygodni architektury, aby wyjaśnić model pamięci i rodzaje instrukcji, które procesor faktycznie wykonuje.

Barry Brown
źródło
Absolutnie. Kiedy masz do czynienia z rejestrami i rozumiesz takie rzeczy, jak bezpośrednie wartości i adresowanie pośrednie, cała koncepcja wskaźników jest intuicyjna. Nauczyłem się montażu 6502, a następnie montażu 68K, zanim napisałem pierwszą linię C, więc zanim zapoznałem się ze wskaźnikami, spojrzałem na (pośredni) zespół kompilatora i było dokładnie tak, jak się spodziewaliśmy.
Radian
2

Moje „aha!” nadszedł moment w tym samouczku:

Samouczek na temat wskaźników i tablic w C

Ściślej mówiąc, przyszedł w tym rozdziale: Rozdział 3: Wskaźniki i łańcuchy

Mówiąc dokładniej, przyszło z tym zdaniem:

Parametr przekazany do puts () jest wskaźnikiem, czyli wartością wskaźnika (ponieważ wszystkie parametry w C są przekazywane przez wartość), a wartość wskaźnika to adres, na który wskazuje, lub po prostu adres .

Kiedy to przeczytałem, chmury się rozdzieliły, a anioły dmuchały fanfarami.

Bo widzisz, że w każdym tutorialu lub książce C, które przeczytałem wcześniej, stwierdziłem, że C może przekazać wartość lub odniesienie, nikczemne kłamstwo. Prawda jest taka, że ​​C zawsze przechodzi przez wartość, ale czasami przekazywana wartość jest czasem adresem. W ramach metody kopia jest tworzona z tego adresu, tak jak kopia byłaby przekazywana przez int. Kopia nie jest tworzona z wartości, na którą wskazuje wskaźnik. Tak więc, używając wskaźnika w ramach metody, możesz uzyskać dostęp do oryginalnej wartości i ją zmienić.

Nigdy nie zostałem programistą C, ale zostałem programistą .NET, a obiekty i odwołania do obiektów działają w ten sam sposób; odwołanie do obiektu jest przekazywane przez wartość (a zatem kopiowane), ale sam obiekt nie jest kopiowany. Pracowałem z wieloma programistami, którzy tego nie rozumieją, ponieważ nigdy nie nauczyli się wskazówek.

Kyralessa
źródło
1

Sztuką jest wyjaśnienie, że położenie rzeczy i sama rzecz nie są takie same, tak jak obrazek rury nie jest rurą . Kiedy coś przenosisz, zmienia się jego lokalizacja. Lokalizacja pozostaje i można tam umieścić coś jeszcze.

sal
źródło
1

Wskaźnik to lepka notatka, która informuje, gdzie jest coś przydatnego. Zawiera lokalizację rzeczy i mówi, jak duża jest rzecz (w każdym razie w C). Podwójny wskaźnik przypomina więc karteczkę z napisem „W lodówce jest sześć paczek”. Musisz zdobyć sześciopak, żeby dowiedzieć się, czy to cola, czy budweiser.

filozofodad
źródło
1

Biorąc pod uwagę kod:

int v=42; // deklarowanie i inicjowanie prostej zmiennej

int *p = &v; // utworzenie punktu p do zmiennej v

O powyższym kodzie można powiedzieć:

int * p // "int pointer p" ... w ten sposób deklarujesz wskaźnik do zmiennej typu int

*p // „wskazywane przez p” ... to dane, na które wskazuje p, to samo co v.

&v // "adres zmiennej v" ... to reprezentuje literalną wartość dla p

zenera
źródło
1

http://cslibrary.stanford.edu/

Ta strona zawiera świetne tutoriale do nauki wskaźników i zarządzania pamięcią.

Sugeruję, abyś zapoznał się z podstawami wskaźników, wskaźnikami i pamięcią podaną na stronie. Możesz także spojrzeć na problemy z listami połączonymi podanymi w łączu, aby dodatkowo wzmocnić koncepcje wskaźnika.

Deovrat Singh
źródło
1

Kluczem do wyjaśnienia wskaźników jest upewnienie się, że osoby, którym się tłumaczysz, już rozumieją pojęcie pamięci. Chociaż byłoby miło, gdyby naprawdę zrozumieli, że jest to niski poziom, wystarczy wierzyć, że pamięć istnieje jako ogromna tablica, i wystarczy zrozumieć, że można uzyskać dostęp do dowolnej pozycji w tablicy według jej indeksu.

Kolejny krok to koncepcja przekazania lokalizacji indeksu zamiast kopiowania całej pamięci ma sens dla większości ludzi. I to wystarczy, aby większość ludzi zrozumiała, dlaczego wskaźniki są przydatne.

ostatnim krokiem do zrozumienia wskaźników jest wyjaśnienie, w jaki sposób można przekazać jako parametr lokalizację indeksu pamięci dla metody przechowywania lokalizacji indeksu, w której przechowywane są wszystkie dane. Przekonałem się, że dla niektórych osób może to być o krok za daleko.

Gdy ktoś zrozumie te podstawowe kroki, od razu zrozumie, że możesz łączyć wskaźniki w nieskończoność, tak długo, jak będziesz śledzić, ile razy trzeba patrzeć na adresy, aby znaleźć rzeczywisty obiekt danych.

Gdy ktoś złapie wskaźniki, następną rzeczą, którą musi szybko uchwycić, jest różnica między pamięcią stosu a pamięcią stosu i dlaczego wskaźniki, które stos pamięci są niebezpieczne, gdy są przekazywane poza metodą.

Michael Shaw
źródło
0

Pamiętam książkę „C puzzle” lub podobną, którą przeczytałem wyłącznie dlatego, że była to jedna z niewielu książek związanych z programowaniem / komputerami dostępnych w bibliotece, moje rozumienie C było szczątkowe. Rzuciło w ciebie expresison C i poprosiło o zbadanie go, coraz bardziej skomplikowane.

peterchen
źródło
0

Kiedy byłem na uniwersytecie, mój profesor miał kilka naprawdę schludnych slajdów PowerPoint, przedstawiających punkt jako osobną zmienną jako własną ze strzałką do miejsca w pamięci (reprezentowanego jak tablica), a kiedy robiliśmy Listy Połączone, robił to krok - krok po kroku, pokazujący, kiedy zmienia się strzałka, gdy dereferencja wskaźnika jest odkładana itp., nie było sposobu, żeby nie zrozumieć go w ciągu kilku minut. Sama koncepcja jest naprawdę łatwa, ale jej prawidłowe wykonanie lub zastosowanie w praktycznych programach wymaga więcej praktyki.

chiurox
źródło
0

Zanim to zrobię, wyjaśniam, że podczas programowania „wszystko zużywa pamięć” i (statyczny) przydział zmiennych w pamięci. Wyjaśnię również, czym jest adres pamięci oraz związek między przestrzenią pamięci, adresem pamięci i zmiennymi.

Na koniec wyjaśniam, że istnieje typ danych całkowitych i zmienne, typ danych ciąg i zmienne ... i tak dalej, dopóki nie wyjaśnię, że istnieje specjalny typ danych przechowujący adresy pamięci, który ma pustą wartość, np. 0 o "", o nazwie null .

I wreszcie, dynamicznie przydzielane zmienne przez użycie wskaźników.

umlcat
źródło