Co ludzie uważają za trudne w wskaźnikach C? [zamknięte]
Z liczby zamieszczonych tutaj pytań wynika, że ludzie mają dość poważne problemy z poruszaniem się po wskaźnikach i arytmetyce wskaźników.
Jestem ciekaw dlaczego. Nigdy nie sprawiały mi większych problemów (chociaż po raz pierwszy dowiedziałem się o nich w neolicie). Aby napisać lepsze odpowiedzi na te pytania, chciałbym wiedzieć, co ludzie uważają za trudne.
Więc, jeśli zmagasz się ze wskaźnikami, lub ostatnio byłeś, ale nagle "mam to", jakie były aspekty wskaźników, które spowodowały problemy?
29 answers
Podejrzewam, że ludzie za bardzo zagłębiają się w odpowiedzi. Zrozumienie harmonogramu, rzeczywistych operacji PROCESORA lub zarządzania pamięcią na poziomie montażu nie jest tak naprawdę wymagane.
Kiedy uczyłem, znalazłem następujące dziury w zrozumieniu uczniów jako najczęstsze źródło problemów:
- magazyn sterty vs stosu. To po prostu oszałamiające, jak wiele osób tego nie rozumie, nawet w ogólnym sensie.
- stosuj ramki. Tylko ogólne pojęcie o dedykowana sekcja stosu dla zmiennych lokalnych wraz z powodem, dla którego jest to 'stos'... szczegóły, takie jak przechowywanie lokalizacji zwrotnej, szczegóły obsługi wyjątków i poprzednie rejestry, mogą być bezpiecznie pozostawione, dopóki ktoś nie spróbuje zbudować kompilatora.
- "Memory is memory is memory" ("Pamięć to pamięć") - zmienia się tylko, Które wersje operatorów lub ile miejsca kompilator daje na konkretny fragment pamięci. Wiesz, że masz do czynienia z tym problemem, gdy ludzie mówią o "jaka (prymitywna) zmienna X naprawdę jest".
Większość moich uczniów była w stanie zrozumieć uproszczony rysunek kawałka pamięci, ogólnie sekcję zmiennych lokalnych stosu w bieżącym zakresie. Generalnie dawanie wyraźnych fikcyjnych adresów do różnych miejsc pomogło.
Myślę, że podsumowując, mówię, że jeśli chcesz zrozumieć wskaźniki, musisz zrozumieć zmienne i to, czym tak naprawdę są we współczesnych architekturach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-31 16:58:27
Kiedy zacząłem z nimi pracować, największym problemem była składnia.
int* ip;
int * ip;
int *ip;
Wszystkie są takie same.
Ale:
int* ip1, ip2; //second one isn't a pointer!
int *ip1, *ip2;
Dlaczego? ponieważ część" wskaźnik " deklaracji należy do zmiennej, a nie do typu.
I wtedy dereferencja rzeczy używa bardzo podobnej notacji:
*ip = 4; //sets the value of the thing pointed to by ip to '4'
x = ip; //hey, that's not '4'!
x = *ip; //ahh... there's that '4'
Z wyjątkiem sytuacji, w których musisz zdobyć wskaźnik... potem używasz ampersand!
int *ip = &x;
Hura za konsekwencję!
Wtedy, najwyraźniej tylko aby być palantem i udowodnić, jak sprytni są, wielu programistów bibliotek używa wskaźników-do-wskaźników-do-wskaźników, a jeśli oczekują tablicy tych rzeczy, cóż, dlaczego nie po prostu przekazać wskaźnik do tego też.
void foo(****ipppArr);
Aby to wywołać, potrzebuję adresu tablicy wskaźników do wskaźników do wskaźników ints:
foo(&(***ipppArr));
W ciągu sześciu miesięcy, kiedy będę musiał utrzymać ten kod, spędzę więcej czasu próbując dowiedzieć się, co to wszystko oznacza, niż przepisanie od podstaw. (tak, pewnie dostał ta składnia jest błędna - minęło trochę czasu, odkąd zrobiłem coś w C. trochę za tym tęsknię, ale potem jestem trochę masochistą)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-06 07:20:05
Właściwe zrozumienie wskaźników wymaga wiedzy o architekturze maszyny.
Wielu programistów nie wie dzisiaj, jak działa ich maszyna, podobnie jak większość ludzi, którzy wiedzą, jak prowadzić samochód, nie wie nic o silniku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 16:47:29
Kiedy mamy do czynienia ze wskaźnikami, ludzie, którzy się mylą, są powszechnie w jednym z dwóch obozów. Byłem (am?) w obu.
The array[]
crowd
To jest tłum, który wprost nie wie, jak przetłumaczyć notację wskaźnika na notację tablicy (lub nawet nie wie, że są one nawet powiązane). Oto cztery sposoby dostępu do elementów tablicy:
- notacja tablicy (indeksowanie) z nazwa tablicy
- notacja tablicy (indeksowanie) z pointer nazwa
- zapis wskaźnikowy ( * ) z nazwa wskaźnika
- zapis wskaźnikowy ( * ) z nazwa tablicy
int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;
array element pointer
notation number vals notation
vals[0] 0 10 *(ptr + 0)
ptr[0] *(vals + 0)
vals[1] 1 20 *(ptr + 1)
ptr[1] *(vals + 1)
vals[2] 2 30 *(ptr + 2)
ptr[2] *(vals + 2)
vals[3] 3 40 *(ptr + 3)
ptr[3] *(vals + 3)
vals[4] 4 50 *(ptr + 4)
ptr[4] *(vals + 4)
Chodzi o to, że dostęp do tablic za pomocą wskaźników wydaje się dość prosty i prosty, ale można w ten sposób zrobić mnóstwo bardzo skomplikowanych i sprytnych rzeczy. Niektóre z nich mogą pozostawić doświadczonych programistów C/C++ zaskoczonych, nie mówiąc już o niedoświadczonych nowicjuszach.
reference to a pointer
i pointer to a pointer
tłum
Ten {[35] } to świetny artykuł, który wyjaśnia różnicę i z którego będę cytował i kradł jakiś kod:)
Jako mały przykład, może być bardzo trudno zobaczyć, co dokładnie autor chciał zrobić, jeśli natknąłeś się na coś takiego:
//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??
int main()
{
int nvar=2;
int* pvar=&nvar;
func(pvar);
....
return 0;
}
Lub, w mniejszym stopniu, coś takiego:
//function prototype
void func(int** ppInt);
int main()
{
int nvar=2;
int* pvar=&nvar;
func(&pvar);
....
return 0;
}
Więc na koniec dnia, co tak naprawdę rozwiążemy z tym całym bełkotem? Nic.
Teraz widzieliśmy składnię z ptr-to-ptr i ref-to-ptr. Są tam jakieś zalety jednego nad drugim? Obawiam się, że nie. Korzystanie z jednego z oba, dla niektórych programistów są po prostu osobiste preferencje. Niektórzy, którzy używają ref-to-ptr mówią, że składnia jest " czystsza" podczas gdy niektórzy, którzy używają ptr-to-ptr, mówią składnia ptr - to-ptr ułatwia ci, którzy czytają to, co robisz.
Ta złożoność i pozorność (pogrubienie pozorności) wymienność z odniesieniami, co często jest kolejnym zastrzeżeniem wskaźniki i błąd nowicjuszy, sprawia, że zrozumienie wskaźników jest trudne. Ważne jest również, aby zrozumieć, ze względu na zakończenie, że wskaźniki do odniesień są nielegalne w C i C++ z mylących powodów, które biorą cię do lvalue
-rvalue
semantyka.
Jak zauważyła poprzednia odpowiedź, wiele razy będziesz miał po prostu tych gorących programistów, którzy myślą, że są sprytni, używając {[8] } i większość z nas jest prawdopodobnie winna pisania takich okrucieństw, ale to po prostu nie jest dobry kod, a z pewnością nie da się go utrzymać.
Ta odpowiedź okazała się dłuższa niż się spodziewałam...Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 20:14:39
Winię jakość materiałów referencyjnych i ludzi prowadzących nauczanie, osobiście; większość pojęć w C (aleSzczególnie wskaźniki) są po prostu po prostunauczane źle. Ciągle grożę napisaniem własnej książki w języku C (zatytułowanej ostatnią rzeczą, jakiej świat potrzebuje, jest kolejna książka o języku programowania C ), ale nie mam na to czasu ani cierpliwości. Więc siedzę tu i rzucam losowymi cytatami ze standardu w ludzi.
Jest też fakt, że kiedy C był początkowo projektowany, zakładano , że rozumiałeś architekturę Maszyny do dość szczegółowego poziomu tylko dlatego, że nie było sposobu, aby tego uniknąć w codziennej pracy(pamięć była tak ciasna, a procesory tak wolne, że musiałeś zrozumieć, jak to, co napisałeś, wpłynęło na wydajność).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 17:49:36
Istnieje świetny artykuł potwierdzający przekonanie, że wskaźniki są trudne na stronie Joela Spolsky ' ego - niebezpieczeństwa JavaSchools.
[Disclaimer - Nie jestem hejterem Javy per se .]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 16:40:42
Większość rzeczy jest trudniejsza do zrozumienia, jeśli nie jesteś ugruntowany w wiedzy, która jest "pod spodem". Kiedy uczyłem CS 'a, stało się to o wiele łatwiejsze, gdy zacząłem moich uczniów od programowania bardzo prostej "maszyny", symulowanego komputera dziesiętnego z opcodami dziesiętnymi, którego pamięć składała się z rejestrów dziesiętnych i adresów dziesiętnych. Wprowadziliby bardzo krótkie programy, aby na przykład dodać serię liczb, aby uzyskać sumę. Potem pojedynczo go obserwowali, co się dzieje. Mogli trzymać wciśnij klawisz "enter" i obserwuj, jak działa "szybko".
Jestem pewien, że prawie każdy na tak zastanawia się, dlaczego jest to przydatne, aby uzyskać tak podstawowe. Zapominamy, jak to było nie wiedzieć, jak programować. Zabawa z takim zabawkowym komputerem wprowadza koncepcje, bez których nie można programować, takie jak pomysły, że obliczanie jest procesem krok po kroku, przy użyciu niewielkiej liczby podstawowych prymitywów do budowania programów, a także pojęcie zmiennych pamięci jako miejsc, w których przechowywane są liczby, w których adres lub adres są przechowywane. nazwa zmiennej różni się od liczby, którą zawiera. Istnieje różnica między czasem, w którym wchodzi się do programu, a czasem, w którym "uruchamia się". Lubię uczyć się programowania jako przekraczania serii "progów prędkości", takich jak bardzo proste programy, następnie pętle i podprogramy, następnie tablice, następnie sekwencyjne I / o, a następnie wskaźniki i struktura danych. Wszystko to jest o wiele łatwiejsze do nauczenia się przez odniesienie do tego, co komputer naprawdę robi pod spodem.
W końcu, gdy dojdziemy do C, wskaźniki są mylące, choć K & R zrobił bardzo dobrą robotę wyjaśniając je. Sposób, w jaki nauczyłem się ich w C, polegał na tym, jak je czytać - od prawej do lewej. Jak kiedy widzę int *p
w mojej głowie mówię "p
wskazuje na int
". C został wymyślony jako krok w górę od języka asemblacji i to mi się w nim podoba - jest blisko tego "podłoża". Wskazówki, jak Wszystko inne, są trudniejsze do zrozumienia, jeśli nie masz tego uziemienia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 14:39:09
Nie dostałem wskaźników, dopóki nie przeczytałem opisu W K & R. do tego momentu wskaźniki nie miały sensu. Czytałem całą masę rzeczy, gdzie ludzie mówili: "nie ucz się wskazówek, są one mylące i będą boleć głowę i dać tętniaki", więc unikałem tego przez długi czas, i stworzył ten niepotrzebny powietrze trudne-koncepcja.
W Przeciwnym Razie, głównie to, co myślałem było, dlaczego na ziemi chcesz zmienną, że trzeba przejść przez obręcze, aby uzyskać wartość, i jeśli chciałeś przypisać do niego różne rzeczy, musiałeś robić dziwne rzeczy, żeby wprowadzić w nie wartości. Cały sens zmiennej jest coś do przechowywania wartości, pomyślałem, więc dlaczego ktoś chciał, aby to skomplikowane było poza mną. " więc ze wskaźnikiem musisz użyć operatora *
, aby uzyskać jego wartość??? Co to za głupota?", myślałem. Bezcelowe, nie zamierzone.
To było skomplikowane, ponieważ nie rozumiałem, że wskaźnik jest adres do czegoś. Jeśli wyjaśnisz, że jest to Adres, że jest to coś, co zawiera adres do czegoś innego, i że możesz manipulować tym adresem, aby robić przydatne rzeczy, myślę, że to może wyjaśnić zamieszanie.
Klasa, która wymagała używania wskaźników do dostępu/modyfikowania portów na komputerze, używania arytmetyki wskaźników do adresowania różnych miejsc pamięci i patrzenia na bardziej skomplikowany kod C, który zmodyfikował ich argumenty, pozbawiła mnie pomysłu, że wskaźniki były, bez sensu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 22:23:03
Oto przykład wskaźnika / tablicy, który dał mi pauzę. Załóżmy, że masz dwie tablice:
uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];
Twoim celem jest skopiowanie zawartości uint8_t z miejsca docelowego za pomocą memcpy(). Zgadnij, który z poniższych celów osiągnie ten cel:
memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));
The answer (Spoiler Alert!) jest wszystkie. "destination", "&destination " i "&destination[0] " są tymi samymi wartościami. "&destination " to inny typ niż dwa pozostałe, ale nadal jest to ta sama wartość. To samo idzie dla permutacji "źródła".
Na marginesie osobiście wolę pierwszą wersję.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 21:42:21
Powinienem zacząć od stwierdzenia, że C i C++ były pierwszymi językami programowania, których się nauczyłem. Zaczynałem od C, potem dużo uczyłem C++ w szkole, a potem wróciłem do C, by biegle się w nim posługiwać.
Pierwszą rzeczą, która zmieszała mnie z wskaźnikami podczas nauki C, było proste:
char ch;
char str[100];
scanf("%c %s", &ch, str);
To zamieszanie było głównie zakorzenione w wprowadzeniu odniesienia do zmiennej dla OUT argumentów, zanim wskaźniki zostały poprawnie wprowadzone do mnie. Pamiętam, że pominąłem pisanie kilka pierwszych przykładów w C na Manekiny ponieważ były zbyt proste tylko nigdy nie dostać pierwszy program, który napisałem do pracy (najprawdopodobniej z tego powodu).
To, co było w tym mylące, to to, co &ch
właściwie oznaczało, jak również dlaczego str
nie było tego potrzebne.
Po zapoznaniu się z tym, pamiętam, że byłem zdezorientowany co do dynamicznej alokacji. W pewnym momencie zdałem sobie sprawę, że posiadanie wskaźników do danych nie było niezwykle przydatne bez dynamiczna alokacja jakiegoś typu, więc napisałem coś w stylu:
char * x = NULL;
if (y) {
char z[100];
x = z;
}
Aby spróbować dynamicznie przydzielić trochę miejsca. Nie zadziałało. Nie byłam pewna, czy to zadziała, ale nie wiedziałam, jak inaczej to zadziała.
Później dowiedziałem się o malloc
i new
, ale naprawdę wydawały mi się magicznymi generatorami pamięci. Nie wiedziałem, jak mogą działać.
Jakiś czas później znowu uczono mnie rekurencji (wcześniej sam się tego nauczyłem, ale teraz byłem na zajęciach) i zapytał, Jak to działa pod maską-gdzie są przechowywane oddzielne zmienne. Mój profesor powiedział "na stosie" i wiele rzeczy stało się dla mnie jasne. Wcześniej słyszałem ten termin i wcześniej implementowałem stosy oprogramowania. Słyszałem, że inni odnoszą się do" stosu " długo wcześniej, ale zapomniałem o tym.
Mniej więcej w tym czasie zdałem sobie sprawę, że używanie wielowymiarowych tablic w C może być bardzo mylące. Wiedziałem, jak to działa, ale tak łatwo było się w nie wplątać, że postanowiłem spróbować z nimi pracować, kiedy tylko będę mógł. Wydaje mi się, że problem polegał głównie na składni (zwłaszcza na przekazywaniu lub zwracaniu ich z funkcji).
Ponieważ pisałem C++ dla szkoły przez następny rok lub dwa, mam duże doświadczenie w używaniu wskaźników do struktur danych. Tutaj miałem nowy zestaw problemów-mieszanie wskaźników. Chciałbym mieć wiele poziomów wskaźników (rzeczy jak node ***ptr;
) i zawsze potykając się o siebie. Dałbym wskazówkę na błędną liczbę czasu i ostatecznie uciec się do zastanawiania się, ile *
potrzebowałem metodą prób i błędów.
W pewnym momencie dowiedziałem się, jak działa sterta programu (tak jakby, ale na tyle dobra, że nie trzyma mnie już w nocy). Pamiętam, że czytałem, że jeśli spojrzysz kilka bajtów przed wskaźnikiem, który malloc
na pewnym systemie zwraca, możesz zobaczyć, ile danych zostało faktycznie przydzielonych. Zdałem sobie sprawę, że kod w malloc
może poprosić o więcej pamięci z systemu operacyjnego i ta pamięć nie była częścią mojego pliku wykonywalnego pliki. Posiadanie przyzwoitego pomysłu na to, jak działa {[6] } , jest naprawdę przydatne.
Wkrótce po tym poszedłem na zajęcia z montażu, które nie nauczyły mnie tak wiele o wskaźnikach, jak większość programistów prawdopodobnie myśli. Skłoniło mnie to jednak do zastanowienia się nad tym, na jaki montaż może zostać przetłumaczony mój kod. Zawsze starałem się pisać efektywny kod, ale teraz miałem lepszy pomysł, jak to zrobić.
Wzięłam też udział w kilku zajęciach, na których musiałam napisać jakiś lisp. Pisząc lisp nie byłem jeśli chodzi o efektywność, jak byłem w C. miałem bardzo mało pojęcia, na co ten kod może być przetłumaczony, jeśli skompilowany, ale wiedziałem, że wydawało się, że użycie wielu lokalnych nazwanych symboli (zmiennych) ułatwiło sprawę. W pewnym momencie napisałem jakiś kod obracania drzewa AVL w trochę Lispie, że miałem bardzo ciężko pisać w C++ z powodu problemów ze wskaźnikami. Zdałem sobie sprawę, że moja niechęć do tego, co uważałem za nadmierne zmienne lokalne, utrudniła mi pisanie tego i kilka inne programy w C++.
Chodziłem też na zajęcia z kompilatorów. Podczas tej klasy przerzuciłem się do materiału zaawansowania i dowiedziałem się o statycznych single przypisanie (SSA) i martwych zmiennych, co nie jest aż tak ważne, poza tym, że nauczyło mnie, że każdy porządny kompilator wykona przyzwoitą robotę radzenia sobie ze zmiennymi, które nie są już używane. Wiedziałem już, że więcej zmiennych (w tym wskaźników) z poprawnymi typami i dobrymi nazwami pomoże mi zachować rzeczy prosto w głowie, ale teraz wiedziałem też, że unikanie ich ze względu na wydajność było jeszcze głupsze niż mówili mi moi mniej mikrooptimizowani profesorowie.
Więc dla mnie znajomość układu pamięci programu bardzo pomogła. Myślenie o tym, co oznacza mój kod, zarówno symbolicznie, jak i na sprzęcie, pomaga mi. Korzystanie z lokalnych wskaźników, które mają prawidłowy typ, bardzo pomaga. Często piszę kod, który wygląda tak:
int foo(struct frog * f, int x, int y) {
struct leg * g = f->left_leg;
struct toe * t = g->big_toe;
process(t);
Tak, że jeśli spieprzę typ wskaźnika jest bardzo jasne przez błąd kompilatora, jaki jest problem. Gdybym to zrobił:
int foo(struct frog * f, int x, int y) {
process(f->left_leg->big_toe);
I pomyliĺ 'y siÄ ™ tam typy wskaĺşnikăłw, bĹ' Ä ™ d kompilatora byĹ ' by o wiele trudniejszy do wykrycia. Skusiłbym się, aby uciekać się do prób i błędów w mojej frustracji i prawdopodobnie pogorszyć sprawę.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 20:09:02
Patrząc wstecz, były cztery rzeczy, które naprawdę pomogły mi w końcu zrozumieć wskazówki. Wcześniej mogłem z nich korzystać, ale nie do końca je rozumiałem. Oznacza to, że wiedziałem, że jeśli podążę za formularzami, uzyskam pożądane rezultaty, ale nie do końca zrozumiałem "dlaczego" formularzy. Zdaję sobie sprawę, że nie jest to dokładnie to, o co prosiłeś, ale myślę, że jest to przydatne następstwo.
Pisanie procedury, która wzięła wskaźnik na liczbę całkowitą i zmodyfikowała liczbę całkowitą. To dało mi niezbędne formy, na których mogłem zbudować wszelkie mentalne modele działania wskaźników.
Jednowymiarowa dynamiczna alokacja pamięci. Zastanawianie się nad alokacją pamięci 1-D pozwoliło mi zrozumieć koncepcję wskaźnika.
Dwuwymiarowa dynamiczna alokacja pamięci. Zastanawianie się nad alokacją pamięci 2-D wzmocniło tę koncepcję, ale także nauczyło mnie, że sam wskaźnik wymaga pamięci i musi być brany pod uwagę.
Różnice między zmienne stosu, zmienne globalne i pamięć sterty. Rozgryzanie tych różnic nauczyło mnie rodzajów pamięci, do których wskaźniki wskazują/odnoszą się.
Każdy z tych elementów wymagał wyobrażenia sobie, co dzieje się na niższym poziomie -- zbudowania modelu mentalnego, który zaspokoi każdą sprawę, którą mogłem rzucić na nią. Wymagało to czasu i wysiłku, ale było warto. Jestem przekonany, że aby zrozumieć wskazówki, trzeba budować ten model mentalny na tym, jak działają i jak są wdrożone.
A teraz wróć do pierwotnego pytania. Bazując na poprzedniej liście, było kilka pozycji, które miałem trudności w uchwyceniu pierwotnie.
- jak i dlaczego można użyć wskaźnika.
- Czym się różnią, a jednocześnie są podobne do tablic.
- zrozumienie, gdzie przechowywane są informacje o wskaźniku.
- zrozumienie, na co i gdzie wskazuje wskaźnik.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 18:06:29
Mój "moment wskaźnikowy" pracował nad niektórymi programami telefonicznymi w C. musiałem napisać emulator wymiany AXE10 za pomocą analizatora protokołu, który rozumiał tylko klasyczne C. wszystko zależało od znajomości wskaźników. Próbowałem napisać mój kod bez nich (Hej, byłem "pre-pointer" wyciąć mi trochę luzu) i nie udało się całkowicie.
Kluczem do ich zrozumienia był dla mnie operator & (address). Kiedy zrozumiałem, że &i
oznacza "adres ja", a następnie zrozumiałem, że *i
oznacza " zawartość z adresu wskazanego przez mnie " przyszedł nieco później. Za każdym razem, gdy pisałem lub czytałem mój kod, zawsze powtarzałem, co oznaczało " & "i co oznaczało" * " i ostatecznie zacząłem używać ich intuicyjnie.
Ku mojemu wstydowi, zostałem zmuszony do VB, a następnie Java, więc moja wiedza o wskaźnikach nie jest tak ostra, jak kiedyś, ale cieszę się, że jestem "post-wskaźnikiem". Nie proś mnie o korzystanie z biblioteki, która wymaga ode mnie zrozumienia **ale P.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 20:29:25
Główną trudnością ze wskaźnikami, przynajmniej dla mnie, jest to, że nie zacząłem od C. zacząłem od Javy. Całe pojęcie wskaźników były naprawdę obce, aż do kilku klas w college ' u, gdzie oczekiwano, że będę znał C. Więc potem nauczyłem się podstaw C i jak używać wskaźników w ich bardzo podstawowym znaczeniu. Nawet wtedy, za każdym razem, gdy czytam kod C, muszę sprawdzić składnię wskaźnika.
Więc z mojego bardzo ograniczonego doświadczenia(1 rok realnego świata + 4 na studiach), wskaźniki zdezorientuj mnie, bo nigdy nie musiałem go używać w niczym innym niż w klasie. A ja mogę współczuć uczniom zaczynającym od CS ' a z Javą zamiast C lub c++. Jak powiedziałeś, nauczyłeś się wskaźników w epoce "neolitu" i prawdopodobnie używasz go od tego czasu. Dla nas, nowszych ludzi, pojęcie przydzielania pamięci i arytmetyki wskaźników jest naprawdę obce, ponieważ wszystkie te języki zostały abstrakcyjne.
P. S. Po przeczytaniu eseju Spolsky ' ego, jego opis "JavaSchools" nie przypominał tego, przez co przeszedłem w college ' u w Cornell ('05-'09). Wziąłem struktury i programowanie funkcyjne (sml), systemy operacyjne (C), algorytmy (pióro i papier) i całą masę innych klas, które nie były nauczane w Javie. Jednak wszystkie klasy intro i electives zostały wykonane w Javie, ponieważ nie ma wartości w nie wymyślanie koła, gdy próbujesz zrobić coś wyższego poziomu niż implementacja hashtable ze wskaźnikami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 16:58:38
Oto Brak odpowiedzi: Użyj cdecl (lub C++decl), aby to rozgryźć:
eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 21:31:40
Dodają dodatkowy wymiar do kodu bez znaczącej zmiany składni. Pomyśl o tym:
int a;
a = 5
Jest tylko jedna rzecz do zmiany: a
. Możesz napisać a = 6
, a wyniki są oczywiste dla większości ludzi. Ale teraz rozważ:
int *a;
a = &some_int;
Istnieją dwie rzeczy o a
, które są istotne w różnych momentach: rzeczywista wartość a
, Wskaźnik i wartość "za" wskaźnikiem. Możesz zmienić a
:
a = &some_other_int;
...i some_int
jest nadal w pobliżu gdzieś o tej samej wartości. Ale można też zmienić to, na co wskazuje:
*a = 6;
Istnieje różnica pojęciowa między a = 6
, która ma tylko lokalne skutki uboczne, a *a = 6
, która może mieć wpływ na wiele innych rzeczy w innych miejscach. Chodzi mi o to, żeNie , że pojęcie indirection jest z natury trudne, ale dlatego, że możesz zrobićzarówno bezpośrednią, lokalną rzecz z a
, jak i pośrednią rzecz z *a
... to może być to, co myli ludzi.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 15:59:50
Programowałem w C++ przez jakieś 2 lata, a następnie konwertowałem na Javę (5 lat) i nigdy nie oglądałem się za siebie. Jednak, kiedy ostatnio musiałem użyć kilku rodzimych rzeczy, dowiedziałem się (ze zdumieniem), że nie zapomniałem nic o wskaźnikach, a nawet uważam je za łatwe w użyciu. Jest to wyraźny kontrast do tego, czego doświadczyłem 7 lat temu, kiedy po raz pierwszy próbowałem zrozumieć tę koncepcję. Czyli zrozumienie i upodobanie to kwestia dojrzałości programistycznej ? :)
LUB
Wskaźniki są jak jazda na rowerze, kiedy już wiesz, jak z nimi pracować, nie można o tym zapomnieć.
Podsumowując, trudno pojąć, czy nie, cała idea wskaźnika jest bardzo edukacyjna i uważam, że powinna być zrozumiana przez każdego programistę, niezależnie od tego, czy programuje na języku ze wskaźnikami, czy nie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 10:20:44
Wskaźniki są trudne ze względu na indirection.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 19:35:09
Wskaźniki są sposobem radzenia sobie z różnicą między uchwytem do obiektu a samym obiektem. (ok, niekoniecznie przedmioty, ale wiesz co mam na myśli, a także gdzie jest mój umysł)
W pewnym momencie prawdopodobnie będziesz musiał poradzić sobie z różnicą między tymi dwoma. W nowoczesnym języku wysokiego poziomu jest to rozróżnienie między kopiowaniem po wartości i kopiowaniem po referencji. Tak czy inaczej, jest to koncepcja, która często jest trudna do zrozumienia dla programistów.
Jednak, jak było zauważono, że składnia obsługi tego problemu w C jest brzydka, niespójna i myląca. W końcu, jeśli naprawdę spróbujesz to zrozumieć, wskaźnik nabierze sensu. Ale kiedy zaczynasz radzić sobie ze wskaźnikami do wskaźników, i tak dalej ad nauseum, robi się to naprawdę mylące zarówno dla mnie, jak i dla innych ludzi.
Kolejną ważną rzeczą do zapamiętania o wskaźnikach jest to, że są one niebezpieczne. C jest językiem mistrza programisty. Zakłada, że wiesz, co robisz i w ten sposób daje Ci moc, aby naprawdę wszystko zepsuć. Podczas gdy niektóre rodzaje programów nadal muszą być napisane w języku C, większość programów tego nie robi, a jeśli masz język, który zapewnia lepszą abstrakcję dla różnicy między obiektem a jego uchwytem, sugeruję, abyś go użył.
Rzeczywiście, w wielu nowoczesnych aplikacjach C++ często zdarza się, że każda wymagana arytmetyka wskaźników jest zamknięta i abstrakcyjna. Nie chcemy, aby deweloperzy robili arytmetykę wskaźników wszędzie. Chcemy scentralizowane, dobrze przetestowane API, które wykonuje arytmetykę wskaźników na najniższym poziomie. Wprowadzanie zmian w tym kodzie musi odbywać się z wielką starannością i intensywnymi testami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 21:19:56
Myślę, że jednym z powodów, dla których wskaźniki C są trudne, jest to, że łączą kilka pojęć, które nie są tak naprawdę równoważne; jednak, ponieważ wszystkie są realizowane za pomocą wskaźników, ludzie mogą mieć trudności z rozłączeniem tych pojęć.
W C, wskaźniki są używane do, amount inne rzeczy:
- Definiowanie rekurencyjnych struktur danych
W C zdefiniowałbyś podlinkowaną listę liczb całkowitych w ten sposób:
struct node {
int value;
struct node* next;
}
Wskaźnik jest tam tylko dlatego, że jest to jedyny sposób aby zdefiniować rekurencyjną strukturę danych w C, gdy koncepcja nie ma nic wspólnego z tak niskopoziomowym szczegółem, jak adresy pamięci. Rozważmy następujący odpowiednik w Haskell, który nie wymaga użycia wskaźników:
data List = List Int List | Null
Dość proste - lista jest albo pusta, albo utworzona z wartości i reszty listy.
- iteracja nad łańcuchami i tablicami
Oto jak możesz zastosować funkcję foo
do każdego znaku ciągu w C:
char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }
Pomimo używania wskaźnika jako iteratora, ten przykład ma niewiele wspólnego z poprzednim. Tworzenie iteratora, który można zwiększyć, jest inną koncepcją niż definiowanie rekurencyjnej struktury danych. Żadna z koncepcji nie jest szczególnie związana z ideą adresu pamięci.
- osiągnąć polimorfizm
Oto prawdziwa sygnatura funkcji znaleziona w glib :
typedef struct g_list GList;
void g_list_foreach (GList *list,
void (*func)(void *data, void *user_data),
void* user_data);
Whoa! To dość gęba void*
' s. A wszystko po to, aby zadeklarować funkcję, która iteruje nad listą, która może zawierać dowolne rzeczy, stosując funkcję do każdego członka. Porównaj to z tym, jak map
jest zadeklarowane w Haskell:
map::(a->b)->[a]->[b]
Jest to funkcja, która przyjmuje funkcję, która przekształca a
na b
i stosuje ją do listy a
' S, aby uzyskać listę b
's. Podobnie jak w funkcji C g_list_foreach
, map
nie musi wiedzieć nic we własnej definicji o typach do które zostaną zastosowane.
Podsumowując:
Myślę, że wskaźniki C byłyby o wiele mniej mylące, gdyby ludzie po raz pierwszy dowiedzieli się o rekurencyjnych strukturach danych, iteratorach, polimorfizmie itp. jako oddzielne pojęcia, a następnie nauczył się , Jak wskaźniki mogą być używane do realizacji tych pomysłów w C, a nie mieszania wszystkich tych pojęć razem w jeden temat "wskaźniki".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-10 14:08:46
Myślę, że wymaga solidnego fundamentu, prawdopodobnie z poziomu maszyny, z wprowadzeniem do jakiegoś kodu maszynowego, montażu i jak reprezentować elementy i strukturę danych w pamięci RAM. Zajmuje to trochę czasu, trochę pracy domowej lub praktyki rozwiązywania problemów i trochę myślenia.
Ale jeśli osoba zna języki wysokiego poziomu na początku (co nie jest nic złego-stolarz używa siekiery. osoba, która musi podzielić atom używa czegoś innego. potrzebujemy ludzi, którzy są stolarzami, a my mamy ludzi, którzy Studiuj atomy) i ta osoba, która zna język wysokiego poziomu, otrzymuje 2-minutowe wprowadzenie do wskaźników, a następnie trudno oczekiwać, że zrozumie arytmetykę wskaźników, wskaźniki do wskaźników, tablicę wskaźników do ciągów o zmiennej wielkości i tablicę znaków itp. Solidny fundament na niskim poziomie może bardzo pomóc.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 16:56:32
Problem, który zawsze miałem (głównie samouk), to "kiedy" użyć wskaźnika. Mogę owinąć głowę wokół składni do konstruowania wskaźnika, ale muszę wiedzieć, w jakich okolicznościach wskaźnik powinien być używany.
Czy tylko ja mam takie nastawienie? ;-)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 18:56:52
Wskaźniki (wraz z innymi aspektami pracy na niskim poziomie) wymagają od użytkownika odebrania magii.
Większość programistów wysokiego poziomu lubi magię.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 19:52:36
Dawno, dawno temu... Mieliśmy 8 bitowe mikroprocesory i wszyscy pisali w montażu. Większość procesorów zawierała pewien rodzaj adresacji pośredniej używanej w tabelach skoków i jądrach. Kiedy pojawiły się języki wyższego poziomu, dodajemy cienką warstwę abstrakcji i nazywamy je wskaźnikami. Z biegiem lat coraz bardziej oddalaliśmy się od sprzętu. To niekoniecznie jest złe. Nie bez powodu nazywane są językami wyższego poziomu. Tym bardziej mogę skoncentrować się na tym, co chcę robić zamiast szczegóły tego, jak to się robi, tym lepiej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-26 20:25:38
Wydaje się, że wielu uczniów ma problem z pojęciem indirection, zwłaszcza gdy spotykają się z pojęciem indirection po raz pierwszy. Pamiętam z czasów, gdy byłem studentem, że z + 100 studentów mojego kursu tylko garstka ludzi naprawdę rozumiała wskazówki.
Pojęcie indirection nie jest czymś, czego często używamy w prawdziwym życiu, a zatem jest to początkowo trudne do uchwycenia pojęcie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 16:52:22
Niedawno miałem właśnie moment kliknięcia wskaźnika i byłem zaskoczony, że znalazłem to mylące. Było bardziej, że wszyscy tak dużo o tym mówili, że założyłem, że dzieje się jakaś czarna magia.
Sposób, w jaki to zrozumiałem, był taki. Wyobraź sobie, że wszystkie zdefiniowane zmienne są przydzielane w czasie kompilacji(na stosie). Jeśli chcesz program, który mógłby obsługiwać duże pliki danych, takie jak audio lub obrazy, nie chcesz stałej ilości pamięci dla tych potencjalnych struktur. Więc do czasu uruchomienia należy przypisać określoną ilość pamięci do przechowywania tych danych (na stercie).
Gdy masz swoje dane w pamięci, nie chcesz kopiować tych danych po całej magistrali pamięci za każdym razem, gdy chcesz uruchomić na niej operację. Powiedz, że chcesz zastosować filtr do danych obrazu. Wskaźnik rozpoczyna się z przodu danych przypisanych do obrazu, a funkcja biegnie między tymi danymi, zmieniając je na miejscu. Jeśli nie wiedziałeś, co robimy, prawdopodobnie skończyłoby się to tworzeniem duplikatów danych, gdy przepuściłeś je przez operację.
Przynajmniej tak to teraz widzę!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-22 13:11:58
Mówiąc Jako nowy c++ tutaj:
System pointer trochę mi zajęło przetrawienie nie koniecznie ze względu na koncepcję, ale ze względu na składnię C++ w stosunku do Javy. Kilka rzeczy, które uznałem za mylące to:
(1) deklaracja zmiennej:
A a(1);
Vs.
A a = A(1);
Vs.
A* a = new A(1);
I najwyraźniej
A a();
Jest deklaracją funkcji, a nie deklaracją zmiennej. W innych językach, jest w zasadzie tylko jeden sposób, aby zadeklarować zmienna.
(2) ampersand jest używany na kilka różnych sposobów. Jeśli jest
int* i = &a;
Wtedy & A jest adresem pamięci.
OTOH, jeśli jest
void f(int &a) {}
Wtedy &A jest parametrem przekazywanym przez odniesienie.
Chociaż może to wydawać się banalne, może to być mylące dla nowych użytkowników - pochodzę z Javy i jest to język z bardziej jednolitym użyciem operatorów(3) relacja tablica-wskaźnik
Jedna rzecz, która jest nieco frustrująca do zrozumienia jest że wskaźnik
int* i
Może być wskaźnikiem do int
int *i = &n; //
Lub
Może być tablicą do int
int* i = new int[5];
I żeby było lepiej, wskaźniki i tablica nie są wymienne we wszystkich przypadkach i wskaźniki nie mogą być przekazywane jako parametry tablicy.
To podsumowuje niektóre z podstawowych frustracji, jakie miałem z C / C++ i jego wskaźnikami, które IMO, jest znacznie spotęgowane przez fakt, że C / C++ ma wszystkie te specyficzne dla języka dziwactwa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-16 14:14:27
Ja osobiście nie rozumiałem wskaźnika nawet po ukończeniu studiów i po pierwszej pracy. Jedyne, co wiedziałem, to to, że potrzebujesz go do linked list, Binary trees i do przekazywania tablic do funkcji. Taka była sytuacja nawet w mojej pierwszej pracy. Dopiero kiedy zacząłem udzielać wywiadów, zrozumiałem, że koncepcja wskaźnika jest głęboka i ma ogromne wykorzystanie i potencjał. Potem zacząłem czytać K & R i pisać własny program testowy. Moim celem była praca.
At this time Odkryłem, że wskaźniki nie są naprawdę złe ani trudne, jeśli są one nauczane w dobry sposób. Niestety, kiedy uczę się C na studiach, nauczyciel nie był świadomy wskaźnika, a nawet zadania używały mniej wskaźników. Na poziomie absolwenta użycie wskaźnika jest tak naprawdę tylko do tworzenia binarnych drzew i połączonych list. To myślenie, że nie potrzebujesz właściwego zrozumienia wskaźników, aby z nimi pracować, zabija pomysł uczenia się ich.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 06:11:15
. hah.. wszystko o pointer w mojej głowie jest to, że daje adres pamięci, gdzie rzeczywiste wartości niezależnie od jego odniesienia.. więc nie ma w tym żadnej magii.. jeśli nauczysz się jakiegoś montażu, nie będziesz miał tyle problemów z nauczeniem się, jak działają wskaźniki.. dajcie spokój... nawet w Javie wszystko jest odniesieniem..
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-27 15:47:46
Główny problem ludzie nie rozumieją, dlaczego potrzebują wskaźników. Ponieważ nie są jasne, co do stosu i sterty. Dobrze jest zacząć od 16-bitowego asemblera dla x86 z małym trybem pamięci. To pomogło wielu ludziom zrozumieć stos, stertę i "adres". I Bajt:) współcześni Programiści czasem nie potrafią powiedzieć ile bajtów potrzeba do zaadresowania przestrzeni 32 bitowej. Skąd mają pomysł na wskaźniki?
Drugi moment to notacja: deklarujesz wskaźnik jako*, dostajesz adres jako & i to jest nie jest łatwo zrozumieć dla niektórych ludzi.
Ostatnią rzeczą, jaką widziałem, był problem z przechowywaniem: rozumieją stertę i stos, ale nie mogą zrozumieć pojęcia "statycznego".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-31 17:07:33