Osobiście uważam, że zdjęcia ze strzałkami skierowanymi w tę stronę lub takie, które utrudniają zrozumienie wskazówek. Sprawia, że wydają się abstrakcyjnymi, tajemniczymi istotami. Oni nie są.
Jak wszystko inne w twoim komputerze, wskaźniki są liczbami . Nazwa „wskaźnik” to po prostu fantazyjny sposób powiedzenia „zmiennej zawierającej adres”.
Dlatego pozwolę sobie poruszyć różne kwestie, wyjaśniając, jak właściwie działa komputer.
Mamy int
, ma nazwę i
i wartość 5. To jest przechowywane w pamięci. Podobnie jak wszystko zapisane w pamięci, potrzebuje adresu, inaczej nie bylibyśmy w stanie go znaleźć. Powiedzmy, że i
kończy się pod adresem 0x12345678, a jego kolega j
z wartością 6 kończy się tuż po nim. Zakładając 32-bitowy procesor, w którym int wynosi 4 bajty, a wskaźniki 4 bajty, wówczas zmienne są przechowywane w pamięci fizycznej w następujący sposób:
Address Data Meaning
0x12345678 00 00 00 05 // The variable i
0x1234567C 00 00 00 06 // The variable j
Teraz chcemy wskazać na te zmienne. Tworzymy jeden wskaźnik do int int* ip1
, i jeden int* ip2
. Podobnie jak wszystko w komputerze, te zmienne wskaźnikowe są również przydzielane gdzieś w pamięci. Załóżmy, że kończą się one pod następnymi sąsiednimi adresami w pamięci, zaraz po j
. ip1=&i;
Ustawiliśmy wskaźniki tak, aby zawierały adresy wcześniej przydzielonych zmiennych: („skopiuj adres i do ip1”) i ip2=&j
. To, co dzieje się między wierszami, to:
Address Data Meaning
0x12345680 12 34 56 78 // The variable ip1(equal to address of i)
0x12345684 12 34 56 7C // The variable ip2(equal to address of j)
Więc to, co otrzymaliśmy, to jeszcze jakieś 4-bajtowe fragmenty pamięci zawierające liczby. Nigdzie w zasięgu wzroku nie ma mistycznych ani magicznych strzał.
W rzeczywistości, patrząc na zrzut pamięci, nie możemy stwierdzić, czy adres 0x12345680 zawiera znak int
lub int*
. Różnica polega na tym, w jaki sposób nasz program korzysta z treści przechowywanych pod tym adresem. (Zadaniem naszego programu jest właściwie po prostu powiedzieć procesorowi, co ma zrobić z tymi liczbami.)
Następnie dodajemy kolejny poziom pośrednictwa za pomocą int** ipp = &ip1;
. Znowu otrzymujemy po prostu kawałek pamięci:
Address Data Meaning
0x12345688 12 34 56 80 // The variable ipp
Wzór wydaje się znajomy. Kolejny fragment 4 bajtów zawierający liczbę.
Teraz, gdybyśmy mieli zrzut pamięci powyższej fikcyjnej małej pamięci RAM, moglibyśmy ręcznie sprawdzić, gdzie wskazują te wskaźniki. Sprawdzamy, co jest przechowywane pod adresem ipp
zmiennej i znajdujemy zawartość 0x12345680. Który jest oczywiście adresem, pod którym ip1
jest przechowywany. Możemy udać się pod ten adres, sprawdzić tam zawartość i znaleźć adres i
, a na koniec udać się pod ten adres i znaleźć numer 5.
Więc jeśli weźmiemy zawartość ipp, *ipp
otrzymamy adres zmiennej wskaźnikowej ip1
. Pisząc *ipp=ip2
, kopiujemy ip2 do ip1, jest to równoważne ip1=ip2
. W obu przypadkach otrzymalibyśmy
Address Data Meaning
0x12345680 12 34 56 7C // The variable ip1
0x12345684 12 34 56 7C // The variable ip2
(Te przykłady zostały podane dla procesora typu big endian)
ipp
przy definiowaniu, aby Twoje pytanie było kompletne ;-)