Reklama
Niezależnie od tego, czy zdajesz sobie z tego sprawę, czy też nie, ogromna większość używanych programów wykorzystuje wskaźniki w pewien sposób. Może doświadczyłeś NullPointerException w pewnym momencie. Jako programista kod, który piszesz, z dużym prawdopodobieństwem użyje wskaźników, nawet jeśli sam ich nie zaimplementowałeś.
Dzisiaj pokażę ci, jak działają wskaźniki, więc możesz spróbować jak działają tablice i listy Jak działają tablice i listy w PythonieTablice i listy są jednymi z najbardziej przydatnych struktur danych w programowaniu - chociaż niewiele osób wykorzystuje je w pełni. Czytaj więcej dla primera programującego. Ten artykuł będzie bardziej oparty na teorii niż zwykle, ale trzymaj się go, wskaźniki są bardzo złożone!
Kod kompilacji
Przed zagłębieniem się w wskaźniki musisz zrozumieć, w jaki sposób budowany i wykonywany jest kod - być może już o tym wiesz. Ta sekcja będzie miała dość ogólne stwierdzenia - rzeczy, które dotyczą większość języków, ale niekoniecznie wszystkich.
Wróćmy do rzeczy. Każdy komputer używa plików binarnych Co to jest binarny? [Technologia wyjaśniona]Biorąc pod uwagę, że pliki binarne są tak absolutnie fundamentalne dla istnienia komputerów, wydaje się dziwne, że nigdy wcześniej nie zajmowaliśmy się tym tematem - więc dzisiaj pomyślałem, że dam krótki przegląd tego, co binarne ... Czytaj więcej , ciąg zer i jedynek, które tworzą nowoczesną technologię, jaką znamy. Niezwykle trudno jest napisać cokolwiek w formacie binarnym (pliki byłyby bardzo mylące), ponieważ są to surowe instrukcje potrzebne jednostka centralna lub Procesor do działania Co to jest procesor i co robi?Akronimy komputerowe są mylące. Co to właściwie jest procesor? Czy potrzebuję procesora czterordzeniowego lub dwurdzeniowego? Co powiesz na AMD lub Intel? Jesteśmy tutaj, aby pomóc wyjaśnić różnicę! Czytaj więcej . Jest to znane jako Kod maszynowy.
Kolejny krok od kodu maszynowego to montaż. Jest to format czytelny dla człowieka. Chociaż programowanie jest wciąż skomplikowane, jest możliwe. Zestaw składa się z szeregu prostych poleceń służących do wykonywania zadań i jest znany jako niski poziom język programowania. Możliwe jest pisanie skomplikowanych programów, ale trudno jest wyrazić abstrakcyjne pojęcia i wymaga dużo uwagi.
Wiele gier wideo i aplikacji o wysokiej wydajności ma pewną logikę zapisaną w asemblerze, ponieważ pewne rzeczywiste wzrosty prędkości można znaleźć, jeśli wiesz, co robisz. Jednak w zdecydowanej większości projektów programistycznych nie musisz znać żadnego zestawu.
Więc jeśli kod maszynowy jest zbyt trudny do napisania, a montaż jest zbyt trudny do zaprogramowania, czym piszesz kod? Oto gdzie wysoki poziom języki wchodzą. Języki wysokiego poziomu ułatwiają pisanie programów. Możesz programować w czymś, co przypomina Twój język ojczysty i łatwo jest wyrazić złożone algorytmy. Być może słyszałeś o wielu językach wysokiego poziomu (i na pewno skorzystałeś z napisanego w nich programu):
- PODSTAWOWY
- C ++
- Seplenienie
Te języki są teraz bardzo stare i wiele z nich zostało opracowanych na początku lat 50. XX wieku! Niemal każdy współczesny język programowania jest językiem wysokiego poziomu, w tym PHP i Python. Każdego dnia wymyślanych jest więcej języków (choć prawdopodobnie jest ich teraz wystarczająco dużo), ale jak dokładnie twój kod nadal działa poprawnie, jeśli komputery wymagają kodu maszynowego?
Tutaj pojawia się kompilacja. Kompilator to program, który konwertuje kod wysokiego poziomu do postaci, którą można wykonać. Może to być kolejny język wysokiego poziomu, ale zwykle jest to asembler. Niektóre języki (takie jak Python lub Java) konwertują kod na etap pośredni o nazwie kod bajtowy. Będzie to wymagać ponownej kompilacji w późniejszym terminie, co zwykle odbywa się na żądanie, na przykład podczas uruchamiania programu. Jest to znane jako w samą porę kompilacja i jest dość popularna.
Zarządzanie pamięcią
Teraz, gdy wiesz, jak działają języki programowania, przyjrzyjmy się zarządzaniu pamięcią w językach wysokiego poziomu. Do tych przykładów będę używać pseudo kod - kod napisany nie w żadnym konkretnym języku, ale używany do wyświetlania pojęć, a nie dokładnej składni. Dzisiaj będzie to w większości przypominać C ++, ponieważ jest to najlepszy język wysokiego poziomu (moim zdaniem).
W tej sekcji pomoże Ci to, jeśli rozumiesz jak działa pamięć RAM Szybki i brudny przewodnik po pamięci RAM: co musisz wiedziećPamięć RAM jest kluczowym elementem każdego komputera, ale może być myląca. Rozbijamy to na łatwe do zrozumienia warunki, które zrozumiesz. Czytaj więcej .
Większość języków ma zmienne - kontenery, które przechowują niektóre dane. Musisz jawnie zdefiniować typ danych. Niektóre dynamicznie pisane języki, takie jak Python lub PHP, obsługują to za Ciebie, ale nadal muszą to robić.
Powiedz, że masz zmienną:
int myNumber;
Ten kod deklaruje zmienną o nazwie mój numeri nadaje mu typ danych liczba całkowita. Po skompilowaniu komputer interpretuje to polecenie jako:
„Znajdź trochę pustej pamięci i zarezerwuj wystarczająco dużo miejsca, aby zapisać liczbę całkowitą”
Po wykonaniu tego polecenia ten bit pamięci nie może być używany przez inny program. Nie zawiera jeszcze żadnych danych, ale jest zarezerwowany dla zmiennej myNumber.
Teraz przypisz wartość do swojej zmiennej:
myNumber = 10;
Aby wykonać to zadanie, komputer uzyskuje dostęp do zarezerwowanej lokalizacji pamięci i zmienia dowolną zapisaną tam wartość na tę nową wartość.
Teraz wszystko jest dobrze i dobrze, ale w jaki sposób lokalizacje pamięci stają się niezarezerwowane? Jeśli programy zarezerwują całą pamięć, którą lubią, pamięć RAM zapełni się natychmiast - to by było bardzo wolny system.
Aby uniknąć tego potencjalnego problemu, wiele języków implementuje Śmieciarz, używane do niszczenia zmiennych (a zatem zwalniania zarezerwowanych lokalizacji pamięci), które zniknęły poza zakresem.
Być może zastanawiasz się, co to jest zakres i dlaczego jest tak ważny. Zakres określa limity i żywotność zmiennych lub dowolnej pamięci używanej przez program. Zmienna jest „poza zakresem”, gdy żaden kod nie może uzyskać do niej dostępu (wtedy wkracza moduł wyrzucający elementy bezużyteczne). Oto przykład:
function maths () {int firstNumber = 1; } int secondNumber = 2; print (firstNumber + secondNumber); // nie będzie działać
Ten przykład nie zostanie skompilowany. Zmienna firstNumber jest w obrębie matematyka funkcja, więc to jest zakres. Nie można uzyskać do niego dostępu spoza funkcji, w której został zadeklarowany. To ważna koncepcja programowaniai zrozumienie, że jest to kluczowe dla pracy ze wskaźnikami.
Ten sposób obsługi pamięci nazywa się stos. Tak działa ogromna większość programów. Nie musisz rozumieć wskaźników, aby z niego korzystać, i jest dość dobrze zorganizowany. Wadą stosu jest szybkość. Ponieważ komputer musi przypisać pamięć, śledzić zmienne i uruchomić wyrzucanie elementów bezużytecznych, istnieje niewielki narzut. Jest to dobre w przypadku mniejszych programów, ale co z wysokowydajnymi zadaniami lub aplikacjami obciążającymi dane?
Wpisz: wskaźniki.
Wskaźniki
Na powierzchni wskaźniki wydają się proste. Odnoszą się (wskaż na) miejsce w pamięci. Może się to nie różnić od „zwykłych” zmiennych na stosie, ale wierzcie mi, jest ogromna różnica. Wskaźniki są przechowywane w sterta. Jest to przeciwieństwo stosu - jest mniej zorganizowany, ale jest znacznie szybszy.
Zobaczmy, jak zmienne są przypisywane na stosie:
liczba całkowita Jeden = 1; int numberTwo = numberOne;
To jest prosta składnia; Zmienna numer dwa zawiera numer jeden. Jego wartość jest kopiowana podczas zadania z numer jeden zmienna.
Jeśli chcesz zdobyć adres pamięci zmiennej, zamiast jej wartości, musisz użyć znaku handlowego i (&). To się nazywa adres operatora i jest niezbędną częścią zestawu narzędzi wskaźnika.
liczba całkowita Jeden = 1; int numberTwo = & numberOne;
Teraz numer dwa zmienna zwrotnica do miejsca w pamięci, zamiast kopiować numer jeden do jego własnej, nowej lokalizacji w pamięci. Jeśli miałbyś wypisać tę zmienną, nie byłby to numer jeden (nawet jeśli jest przechowywany w miejscu pamięci). Wypisałoby to lokalizację pamięci (prawdopodobnie coś w rodzaju 2167, choć różni się w zależności od systemu i dostępnej pamięci RAM). Aby uzyskać dostęp do wartości zapisanej we wskaźniku, zamiast miejsca w pamięci, musisz dereferencja wskaźnik. Umożliwia to bezpośredni dostęp do wartości, która w tym przypadku byłaby numerem jeden. Oto jak wyreżyserujesz wskaźnik:
int numberTwo = * numberOne;
The operator dereferencji jest gwiazdką (*).
To może być trudna do zrozumienia koncepcja, więc omówmy to jeszcze raz:
- The adres operator (&) przechowuje adres pamięci.
- The operator dereferencji (*) uzyskuje dostęp do wartości.
Składnia zmienia się nieznacznie podczas deklarowania wskaźników:
int * myPointer;
Typ danych int tutaj odnosi się do typu danych wskaźnika zwrotnica do, a nie typ samego wskaźnika.
Teraz, gdy wiesz, jakie są wskaźniki, możesz z nimi zrobić naprawdę fajne sztuczki! Gdy używana jest pamięć, uruchamia się system operacyjny sekwencyjnie. Możesz myśleć o RAM jako o dziurawych gołębiach. Wiele otworów do przechowywania czegoś, tylko jeden może być używany jednocześnie. Różnica polega na tym, że wszystkie gołębie są ponumerowane. Podczas przypisywania pamięci system operacyjny zaczyna się od najniższego numeru i działa. Nigdy nie przeskakuje między liczbami losowymi.
Podczas pracy ze wskaźnikami, jeśli przypisałeś tablicę, możesz łatwo przejść do następnego elementu, po prostu zwiększając wskaźnik.
Tutaj robi się ciekawie. Gdy przekazujesz wartości do funkcji (używając zmiennych przechowywanych na stosie), wartości te są kopiowane do twojej funkcji. Jeśli są to duże zmienne, program przechowuje je teraz dwukrotnie. Po zakończeniu funkcji konieczne może być zwrócenie tych wartości. Funkcje mogą generalnie zwracać tylko jedną rzecz - co z tego, jeśli chcesz zwrócić dwie, trzy lub cztery rzeczy?
Jeśli przekażesz wskaźnik do swojej funkcji, tylko adres pamięci zostanie skopiowany (co jest małe). To oszczędza procesorowi dużo pracy! Może twój wskaźnik wskazuje na ogromną tablicę obrazów - nie tylko twoja funkcja może działać dokładnie tak samo dane przechowywane w dokładnie tej samej lokalizacji w pamięci, ale gdy to zrobisz, nie ma potrzeby powrotu byle co. Schludny!
Musisz jednak być bardzo ostrożny. Wskaźniki mogą nadal wykraczać poza zakres i być zbierane przez moduł odśmiecający. Wartości przechowywane w pamięci nie są jednak gromadzone. Nazywa się to wyciekiem pamięci. Nie możesz już uzyskać dostępu do danych (ponieważ wskaźniki zostały zniszczone), ale wciąż zużywa pamięć. Jest to częsty powód awarii wielu programów i może się spektakularnie zawieść, jeśli istnieje duża ilość danych. W większości przypadków system operacyjny zabija program, jeśli masz duży wyciek (zużywa więcej pamięci RAM niż system), ale nie jest to pożądane.
Wskaźniki debugowania mogą być koszmarem, szczególnie jeśli pracujesz z dużą ilością danych lub pracujesz w pętli. Ich wady i trudność w zrozumieniu są naprawdę warte kompromisów, które zyskujesz na wydajności. Pamiętaj jednak, że nie zawsze mogą być wymagane.
To tyle na dzisiaj. Mam nadzieję, że nauczyłeś się czegoś użytecznego na temat złożonego tematu. Oczywiście nie omówiliśmy wszystkiego, co trzeba wiedzieć - to bardzo złożony temat. Jeśli chcesz dowiedzieć się więcej, bardzo polecam C ++ w ciągu 24 godzin.
Jeśli to było trochę skomplikowane, spójrz na nasz przewodnik po najłatwiejszych językach programowania 6 najłatwiejszych języków programowania dla początkującychNauka programowania polega na znalezieniu odpowiedniego języka tak samo, jak na procesie edycji. Oto sześć najpopularniejszych języków programowania dla początkujących. Czytaj więcej .
Czy nauczyłeś się dzisiaj, jak działają wskaźniki? Czy masz jakieś wskazówki i porady, którymi chcesz się podzielić z innymi programistami? Wskocz do komentarzy i podziel się swoimi przemyśleniami poniżej!
Joe jest absolwentem informatyki na University of Lincoln w Wielkiej Brytanii. Jest profesjonalnym programistą, a kiedy nie lata latającymi dronami ani nie pisze muzyki, często można go znaleźć przy robieniu zdjęć lub tworzeniu filmów.