Zasadniczo rozumiem, jak używać wskaźników, ale nie wiem, jak najlepiej ich używać w celu lepszego programowania.
Jakie są dobre projekty lub problemy do rozwiązania przy użyciu wskaźników, dzięki czemu mogę je lepiej zrozumieć?
Zasadniczo rozumiem, jak używać wskaźników, ale nie wiem, jak najlepiej ich używać w celu lepszego programowania.
Jakie są dobre projekty lub problemy do rozwiązania przy użyciu wskaźników, dzięki czemu mogę je lepiej zrozumieć?
Odpowiedzi:
Manipulowanie dużymi ilościami danych w pamięci to miejsce, w którym wskaźniki naprawdę świecą.
Przekazywanie dużego obiektu przez odniesienie jest równoznaczne z przekazaniem zwykłej starej liczby. Możesz manipulować potrzebnymi częściami w przeciwieństwie do kopiowania obiektu, zmieniania go, a następnie przekazywania kopii w celu umieszczenia na miejscu oryginału.
Koncepcja wskaźnika pozwala odwoływać się do danych według adresu bez powielania przechowywania danych. Takie podejście pozwala na pisanie wydajnych algorytmów, takich jak:
Sortowanie
Podczas przenoszenia danych w algorytmie sortowania możesz przesunąć wskaźnik zamiast samych danych - pomyśl o posortowaniu milionów wierszy na ciągu 100 znaków; oszczędzasz wiele niepotrzebnych ruchów danych.
Listy połączone
Możesz przechowywać lokalizację następnego i / lub poprzedniego elementu, a nie całe dane powiązane z rekordem.
Przekazywanie parametrów
W tym przypadku podajesz adres danych zamiast samych danych. Ponownie pomyśl o algorytmie kompresji nazwy, który działa na milionach wierszy.
Pojęcie to można rozszerzyć na struktury danych, takie jak relacyjne bazy danych, w których wskaźnik przypomina klucz obcy . Niektóre języki nie zachęcają do używania wskaźników, takich jak C # i COBOL.
Przykłady można znaleźć w wielu miejscach, takich jak:
Poniższy post może być w jakiś sposób istotny:
Dziwi mnie, że żadna inna odpowiedź nie wspomniała o tym: wskaźniki pozwalają na tworzenie nieciągłych i nieliniowych struktur danych, w których element może być powiązany z wieloma innymi w złożony sposób.
Połączone listy (pojedynczo, podwójnie i kołowo), drzewa (czerwono-czarny, AVL, trie, binarny, partycjonowanie przestrzeni…) i wykresy to przykłady struktur, które można zbudować w sposób najbardziej naturalny pod względem referencji, a nie tylko wartości .
Jednym prostym sposobem jest polimorfizm. Polimorfizm działa tylko ze wskaźnikami.
Ponadto używasz wskaźników za każdym razem, gdy potrzebujesz dynamicznego przydziału pamięci. W C zwykle dzieje się tak, gdy trzeba przechowywać dane w tablicy, ale nie znasz rozmiaru w czasie kompilacji. Następnie zadzwonisz do malloc, aby przydzielić pamięć i wskaźnik, aby uzyskać do niej dostęp. Niezależnie od tego, czy wiesz, czy nie, kiedy używasz tablicy, używasz wskaźników.
for(int i = 0; i < size; i++)
std::cout << array[i];
jest odpowiednikiem
for(int i = 0; i < size; i++)
std::cout << *(array + i);
Ta wiedza pozwala robić naprawdę fajne rzeczy, takie jak kopiowanie całej tablicy w jednym wierszu:
while( (*array1++ = *array2++) != '\0')
W c ++ używasz new do przydzielania pamięci dla obiektu i przechowywania go we wskaźniku. Robisz to za każdym razem, gdy musisz utworzyć obiekt w czasie wykonywania zamiast w czasie kompilacji (tj. Metoda tworzy nowy obiekt i zapisuje go na liście).
Aby lepiej zrozumieć wskaźniki:
Znajdź niektóre projekty wykorzystujące dziedziczenie.
Oto projekt, na którym wyciąłem zęby:
Wczytaj z pliku dwie macierze nxn i wykonaj na nich podstawowe operacje w przestrzeni wektorowej i wydrukuj ich wynik na ekranie.
Aby to zrobić, musisz użyć dynamicznych tablic i odwołać się do swoich tablic za pomocą wskaźników, ponieważ będziesz mieć dwie tablice tablic (wielowymiarowe tablice dynamiczne). Po zakończeniu tego projektu będziesz miał całkiem dobry pomysł na użycie wskaźników.
Aby naprawdę zrozumieć, dlaczego wskaźniki są ważne, musisz zrozumieć różnicę między alokacją sterty a alokacją stosu.
Oto przykład przydziału stosu:
struct Foo {
int bar, baz
};
void foo(void) {
struct Foo f;
}
Obiekty przydzielone na stosie istnieją tylko przez czas wykonywania bieżącej funkcji. Kiedy wezwanie do foo
wykracza poza zakres, zmienia się również zmienna f
.
Jednym z przypadków, w których staje się to problemem, jest potrzeba zwrócenia czegoś innego niż typ całkowy z funkcji (na przykład struktura Foo z powyższego przykładu).
Na przykład następująca funkcja spowodowałaby tak zwane „niezdefiniowane zachowanie”.
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
struct Foo f;
return &f;
}
Jeśli chcesz zwrócić coś struct Foo *
z funkcji, tak naprawdę potrzebujesz alokacji sterty:
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
return malloc(sizeof(struct Foo));
}
Funkcja malloc
przydziela obiekt na stercie i zwraca wskaźnik do tego obiektu. Zauważ, że termin „obiekt” jest tutaj używany luźno, co oznacza „coś”, a nie obiekt w znaczeniu programowania obiektowego.
Żywotność obiektów przydzielonych na stercie jest kontrolowana przez programistę. Pamięć dla tego obiektu będzie zarezerwowana do momentu zwolnienia go przez programistę, tj. Przez wywołanie free()
lub do zakończenia programu.
Edycja : Nie zauważyłem, że to pytanie jest oznaczone jako pytanie C ++. Operatory C ++ new
i new[]
wykonują tę samą funkcję, co malloc
. Operatory delete
i delete[]
są analogiczne do free
. Choć new
i delete
powinny być wykorzystywane wyłącznie do celów alokacji i wolne C ++ obiektów, zastosowanie malloc
i free
jest całkowicie legalne w kodu C ++.
Napisz dowolny nietrywialny projekt w C, a będziesz musiał dowiedzieć się, jak / kiedy używać wskaźników. W C ++ będziesz używać głównie obiektów z obsługą RAII, które wewnętrznie zarządzają wskaźnikami, ale w C surowe wskaźniki mają znacznie większą rolę. Jeśli chodzi o rodzaj projektu, który powinieneś zrobić, może to być coś nietrywialnego:
Polecam ostatni.
Niemal wszystkie problemy programistyczne, które można rozwiązać za pomocą wskaźników, można rozwiązać za pomocą innych bezpieczniejszych typów referencji (nie odnosząc się do referencji C ++ , ale ogólna koncepcja CS posiadania zmiennej odnosi się do wartości danych przechowywanych gdzie indziej).
Wskaźniki , będące specyficzną implementacją referencji na niskim poziomie, w których można bezpośrednio manipulować adresami pamięci, są bardzo potężne, ale ich użycie może być nieco niebezpieczne (np. Wskazywać lokalizacje pamięci poza programem).
Zaletą bezpośredniego używania wskaźników jest to, że będą one nieco szybsze, bez konieczności przeprowadzania kontroli bezpieczeństwa. Języki takie jak Java, które nie implementują bezpośrednio wskaźników w stylu C, odczują niewielki spadek wydajności, ale zmniejszą wiele rodzajów trudnych do debugowania sytuacji.
Jeśli chodzi o powód, dla którego potrzebujesz pośrednictwa, lista jest dość długa, ale zasadniczo dwie kluczowe idee to:
selected_object
która jest odniesieniem do jednego z obiektów, jest znacznie bardziej wydajne niż kopiowanie wartości bieżącego obiektu do nowej zmiennej.Manipulowanie obrazem na poziomie pikseli jest prawie zawsze łatwiejsze i szybsze przy użyciu wskaźników. Czasami jest to możliwe tylko przy użyciu wskaźników.
Wskaźniki są używane w wielu językach programowania pod powierzchnią, nie denerwując użytkownika. C / C ++ po prostu daje ci do nich dostęp.
Kiedy ich używać: tak często, jak to możliwe, ponieważ kopiowanie danych jest nieefektywne. Kiedy ich nie używać: Gdy chcesz dwie kopie, które można zmienić osobno. (Co w zasadzie zakończy się kopiowaniem zawartości obiektu_1 do innego miejsca w pamięci i zwracaniem wskaźnika - tym razem wskazując na obiekt_2)
Wskaźniki są istotną częścią każdej implementacji struktury danych w C, a struktury danych są istotną częścią każdego nietrywialnego programu.
Jeśli chcesz dowiedzieć się, dlaczego wskaźniki są tak ważne, sugerowałbym zapoznanie się z listą połączoną i spróbowanie jej napisania bez użycia wskaźników. Nie postawiłem ci niemożliwego wyzwania (WSKAZÓWKA: wskaźniki są używane do wskazywania lokalizacji w pamięci, jak odwołujesz się do elementów w tablicach?).
Przykładem może być budowanie portfela zamówień z limitem.
na przykład kanał ITCH 4.1 ma pojęcie „zamień” zamówienia, w których ceny (a zatem i pierwszeństwo) mogą ulec zmianie. Chcesz móc odbierać zamówienia i przenosić je gdzie indziej. Wdrożenie podwójnie zakończonej kolejki ze wskaźnikami sprawia, że operacja jest bardzo łatwa.
Przeglądając różne strony StackExchange, zdaję sobie sprawę, że zadawanie takich pytań jest raczej modne. Będę narażony na krytykę i negatywne opinie. To nie ma na celu trollowania ani płomienia, chcę jedynie pomóc, udzielając uczciwej oceny pytania.
Ta ocena jest następująca: To bardzo dziwne pytanie, które należy zadać programistowi C. Prawie wszystko, co mówi, to „nie znam C.” Jeśli przeanalizuję trochę bardziej i bardziej cynicznie, podpowiedź w następstwie tego „pytania”: „Czy jest jakiś skrót, który mogę wykorzystać, aby szybko i nagle zdobyć wiedzę doświadczonego programisty C, bez poświęcania czasu? do samodzielnego badania? ” Jakakolwiek „odpowiedź”, którą ktoś może udzielić, nie zastąpi zrobienia i wykonania zadania polegającego na zrozumieniu podstawowej koncepcji i jej wykorzystania.
Wydaje mi się, że bardziej konstruktywne jest uczenie się języka C z pierwszej ręki, niż korzystanie z Internetu i zadawanie tego ludziom pytań. Kiedy dobrze znasz C, nie będziesz zadawać sobie trudu z zadawaniem takich pytań, to będzie jak pytanie „jakie problemy najlepiej rozwiązać za pomocą szczoteczki do zębów?”
Mimo że wskaźniki naprawdę świecą podczas pracy z dużymi obiektami pamięci, nadal istnieje sposób, aby zrobić to samo bez nich.
Wskaźnik jest absolutnie niezbędny do tak zwanego programowania dynamicznego , kiedy nie wiesz, ile pamięci potrzebujesz, zanim program się uruchomi. W programowaniu dynamicznym możesz żądać fragmentów pamięci podczas wykonywania i umieszczać w nich własne dane - więc potrzebujesz wskaźnika lub referencji (tutaj różnica nie jest ważna), aby móc pracować z tymi fragmentami danych.
Tak długo, jak możesz zająć określoną pamięć w czasie wykonywania i umieścić swoje dane w nowo pozyskanej pamięci, możesz wykonać następujące czynności:
Możesz mieć samorozszerzające się struktury danych. Są to struktury, które mogą się rozszerzać, żądając dodatkowej pamięci, o ile wyczerpią się ich pojemności. Kluczową właściwością każdej samorozszerzającej się struktury jest to, że składa się ona z małych bloków pamięci (zwanych węzłami, elementów listy itp. W zależności od struktury), a każdy blok zawiera odniesienia do innych bloków. Te „powiązane” struktury budują większość współczesnych typów danych: wykresy, drzewa, listy itp.
Możesz programować za pomocą paradygmatu OOP (Object-Oriented Programming). Cały OOP opiera się na wykorzystaniu zmiennych nie bezpośrednich, ale odniesień do instancji klas (zwanych obiektami) i manipulowaniu nimi. Żadna pojedyncza instancja nie może istnieć bez wskaźników (nawet jeśli możliwe jest użycie klas tylko statycznych, nawet bez wskaźników, jest to raczej wyjątek).
Zabawne, właśnie odpowiedziałem na pytanie dotyczące C ++ i mówiłem o wskaźnikach.
Krótka wersja jest taka, że NIGDY nie potrzebujesz wskaźników, chyba że 1) używana biblioteka zmusza cię 2) Potrzebujesz referencji zerowej.
Jeśli potrzebujesz tablicy, listy, łańcucha itp., Po prostu umieść ją na stosie i użyj obiektu stl. Zwracanie lub przekazywanie obiektów stl jest szybkie (niezaznaczony fakt), ponieważ mają wewnętrzny kod, który kopiuje wskaźnik zamiast obiektu i kopiuje dane tylko, jeśli do niego napiszesz. Jest to zwykły C ++, nawet nowy C ++ 11, który ułatwi pisarzom bibliotek.
Jeśli używasz wskaźnika, upewnij się, że spełnia jeden z tych dwóch warunków. 1) Podajesz dane wejściowe, które mogą być zerowalne. Przykładem jest opcjonalna nazwa pliku. 2) Jeśli chcesz oddać własność. Tak jak w przypadku przekazania lub zwrócenia wskaźnika, nie masz ŻADNYCH jego kopii ani użyć wskazanego wskaźnika
ptr=blah; func(ptr); //never use ptr again for here on out.
Ale nie używałem wskaźników ani inteligentnych wskaźników od bardzo dawna i profilowałem swoją aplikację. Działa bardzo szybko.
DODATKOWA UWAGA: Zauważam, że piszę własne struktury i przekazuję je. Jak to zrobić bez użycia wskaźników? To nie jest kontener STL, więc przechodzenie przez ref jest powolne. Zawsze ładuję moją listę danych / deques / map i tym podobne. Nie pamiętam zwracania żadnych obiektów, chyba że była to jakaś lista / mapa. Nawet sznurka. Spojrzałem na kod dla pojedynczych obiektów i zauważyłem, że robię coś takiego, { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }
więc w zasadzie zwracam obiekty (wiele) lub wstępnie przydzielam / przekazuję referencję do wypełnienia (pojedynczy).
Teraz, jeśli piszesz, jesteś własnym deque, mapą itp., Musisz użyć wskaźników. Ale nie musisz. Pozwól STL i być może zwiększyć martwienie się o to. Musisz tylko napisać dane i rozwiązania. Nie pojemniki do przechowywania;)
Mam nadzieję, że teraz nigdy nie używasz wskaźników: D. Powodzenia w radzeniu sobie z bibliotekami, które do tego zmuszają
Wskaźniki są bardzo przydatne do pracy z urządzeniami zamapowanymi w pamięci. Możesz zdefiniować strukturę, która odzwierciedla (powiedzmy) rejestr kontrolny, a następnie przypisać go do adresu rzeczywistego rejestru kontrolnego w pamięci i bezpośrednio nim manipulować. Możesz także wskazać bezpośrednio bufor transferu na karcie lub układzie scalonym, jeśli MMU zamapowało go w przestrzeni pamięci systemowej.
Widzę wskaźniki jako mój palec wskazujący, używamy kilku rzeczy:
proszę wybacz tę złą odpowiedź
Ponieważ twoje pytanie jest oznaczone jako C ++, odpowiem na twoje pytanie dotyczące tego języka.
W C ++ istnieje rozróżnienie między wskaźnikami a referencjami, dlatego istnieją dwa scenariusze, w których wskaźniki (lub inteligentne wskaźniki) są niezbędne do ułatwienia określonych zachowań. Mogą być używane w innych okolicznościach, jednak pytasz, jak „najlepiej z nich korzystać”, a we wszystkich innych okolicznościach istnieją lepsze alternatywy.
Wskaźnik klasy bazowej umożliwia wywołanie metody wirtualnej zależnej od typu obiektu, na który wskazuje wskaźnik.
Wskaźniki są niezbędne podczas dynamicznego tworzenia obiektu (na stercie zamiast na stosie). Jest to konieczne, jeśli chcesz, aby żywotność tych obiektów była dłuższa niż zakres, w którym zostały utworzone.
Jeśli chodzi o „dobre projekty lub problemy do rozwiązania”, jak już powiedzieli inni, każdy nietrywialny projekt będzie wykorzystywał wskaźniki.