Wskaźnik (programowanie komputerowe)
AdaEdit
Ada jest językiem silnie typizowanym, w którym wpisywane są wszystkie wskaźniki i dozwolone są tylko bezpieczne konwersje typów. Wszystkie wskaźniki są domyślnie inicjowane jako null
, a każda próba uzyskania dostępu do danych przez wskaźnik null
powoduje zgłoszenie wyjątku. Wskaźniki w Adzie nazywane są typami dostępu. Ada 83 nie zezwalała na arytmetykę typów dostępu (chociaż wielu producentów kompilatorów zapewniało to jako niestandardową funkcję), ale Ada 95 obsługuje „bezpieczną” arytmetykę typów dostępu za pośrednictwem pakietu System.Storage_Elements
.
BASICEdit
Kilka starszych wersji BASICa dla platformy Windows obsługiwało STRPTR () zwracające adres ciągu, a VARPTR () zwracające adres zmienną. Visual Basic 5 miał również obsługę OBJPTR (), aby zwrócić adres interfejsu obiektu, oraz dla operatora ADDRESSOF, aby zwrócić adres funkcji. Typy tych wszystkich są liczbami całkowitymi, ale ich wartości są równoważne te utrzymywane przez typy wskaźników.
Nowsze dialekty języka BASIC, takie jak FreeBASIC lub BlitzMax, mają jednak wyczerpujące implementacje wskaźników. We FreeBASIC arytmetyka na ANY
wskaźnikach ( odpowiednik C „s void*
) są traktowane tak, jakby wskaźnik ANY
miał szerokość bajtu. ANY
wskaźników nie może być wyłuskanych, tak jak w C. Ponadto przesyłanie między ANY
a innymi wskaźnikami nie spowoduje żadnych ostrzeżeń.
dim as integer f = 257dim as any ptr g = @fdim as integer ptr i = gassert(*i = 257)assert( (g + 4) = (@f + 1) )
Edycja w C i C ++
W językach C i C ++ wskaźniki to zmienne przechowujące adresy i może być null. Każdy wskaźnik ma typ, na który wskazuje, ale można dowolnie rzutować między typami wskaźników (ale nie między wskaźnikiem funkcji a wskaźnikiem obiektu). Specjalny typ wskaźnika zwany „void pointer” umożliwia wskazywanie dowolnego (nie -funkcja) obiektu, ale jest ograniczony przez fakt, że nie można go bezpośrednio wyłuskać (ma zostać rzucony). Sam adres może być często bezpośrednio manipulowany przez rzutowanie wskaźnika do i z typu integralnego o wystarczającym rozmiarze, chociaż wyniki są zdefiniowane w implementacji i mogą rzeczywiście powodować niezdefiniowane zachowanie; podczas gdy wcześniejsze standardy C nie miały typu całkowitego, który gwarantowałby, że był wystarczająco duży, C99 określa nazwę typu uintptr_t
zdefiniowaną w <stdint.h>
, ale implementacja nie musi tego zapewniać.
C ++ w pełni obsługuje wskaźniki C i rzutowanie typów w C. Obsługuje również nową grupę operatorów rzutowania typów, aby pomóc wyłapać niektóre niezamierzone niebezpieczne rzucanie w czasie kompilacji. Od C ++ 11 biblioteka standardowa C ++ udostępnia również inteligentne wskaźniki (unique_ptr
, shared_ptr
i weak_ptr
), który może być używany w niektórych sytuacjach jako bezpieczniejsza alternatywa dla pierwotnych wskaźników C. C ++ obsługuje również inną formę odwołań, całkiem różną od wskaźnika, nazywaną po prostu referencją lub typem referencyjnym.
Arytmetyka wskaźników, to znaczy możliwość modyfikowania adresu docelowego wskaźnika za pomocą operacji arytmetycznych (jak jak również porównania wielkości), jest ograniczony przez standard języka do pozostawania w granicach pojedynczego obiektu tablicy (lub tuż po nim), iw przeciwnym razie wywoła niezdefiniowane zachowanie. Dodawanie lub odejmowanie od wskaźnika przesuwa go o wielokrotność rozmiaru jego typu danych. Na przykład dodanie 1 do wskaźnika do 4-bajtowych wartości całkowitych spowoduje zwiększenie adresu wskazanego bajtu wskaźnika o 4. Powoduje to zwiększenie wskaźnika tak, aby wskazywał na następny element w ciągłym tablica liczb całkowitych – która często jest zamierzonym wynikiem. Nie można wykonać arytmetyki wskaźników na wskaźnikach void
, ponieważ typ void nie ma rozmiaru, a zatem wskazany adres nie może zostać dodany, chociaż gcc i inne kompilatory będą wykonywać arytmetykę bajtów na void*
jako niestandardowe rozszerzenie, traktując je tak, jakby było char *
.
Arytmetyka wskaźników zapewnia programiście pojedynczy sposób radzenia sobie z różnymi typami: dodawanie i odejmowanie liczby wymaganych elementów zamiast rzeczywistego przesunięcia w bajtach. (Arytmetyka wskaźnika ze wskaźnikami char *
używa przesunięć bajtowych, ponieważ sizeof(char)
z definicji ma wartość 1). W szczególności definicja języka C wyraźnie deklaruje, że składnia a
, która jest n
-tym elementem tablicy a
, jest równoważna do *(a + n)
, czyli zawartość elementu wskazywanego przez a + n
. Oznacza to, że n
jest odpowiednikiem a
i można pisać np. a
lub 3
równie dobrze, aby uzyskać dostęp do czwartego elementu tablicy a
.
Chociaż arytmetyka wskaźników jest potężna, może być źródłem błędów komputerowych. Ma tendencję do dezorientowania początkujących programistów, zmuszając ich do różnych kontekstów: wyrażenie może być zwykłym wyrażeniem arytmetycznym lub wyrażeniem arytmetycznym wskaźnikowym, a czasami łatwo jest jedno z nich pomylić. W odpowiedzi na to wiele współczesnych języków komputerowych wysokiego poziomu (na przykład Java) nie zezwala na bezpośredni dostęp do pamięci przy użyciu adresów. Również bezpieczny dialekt C Cyclone rozwiązuje wiele problemów za pomocą wskaźników. Więcej dyskusji znajdziesz w języku programowania C.
Wskaźnik void
lub void*
jest obsługiwany w ANSI C i C ++ jako ogólny typ wskaźnika. Wskaźnik do void
może przechowywać adres dowolnego obiektu (nie funkcji), a w języku C jest niejawnie konwertowany na dowolny inny typ wskaźnika obiektu przy przypisaniu, ale musi być jawnie rzutowany if dereferenced.K & RC użył char*
dla celu „wskaźnika agnostycznego typu” (przed ANSI C).
C ++ nie pozwala na niejawną konwersję void*
na inne typy wskaźników, nawet w przypisaniach. Była to decyzja projektowa mająca na celu uniknięcie nieostrożnych, a nawet niezamierzonych rzutów, chociaż większość kompilatorów wyświetla tylko ostrzeżenia , a nie błędy, podczas napotkania innych rzutowań.
W C ++ nie ma void&
(odwołanie do void), które uzupełniałoby void*
(wskaźnik do void), ponieważ odwołania zachowują się jak aliasy do zmiennych, na które wskazują, i nigdy nie może istnieć zmienna, której typ to void
.
Omówienie składni deklaracji wskaźnikaEdytuj
Te deklaracje wskaźników c nad większością wariantów deklaracji wskaźnika. Oczywiście można mieć potrójne wskaźniki, ale główne zasady stojące za potrójnym wskaźnikiem istnieją już w podwójnym wskaźniku.
() i mają wyższy priorytet niż *.
C # Edit
W języku programowania C # wskaźniki są obsługiwane tylko pod pewnymi warunkami: każdy blok kodu, w tym wskaźniki, musi być oznaczony słowem kluczowym unsafe
. Takie bloki zwykle wymagają wyższych uprawnień bezpieczeństwa, aby mogły zostać uruchomione. Składnia jest zasadniczo taka sama jak w C ++, a wskazany adres może być pamięcią zarządzaną lub niezarządzaną. Jednak wskaźniki do pamięci zarządzanej (dowolny wskaźnik do obiektu zarządzanego) muszą być zadeklarowane przy użyciu słowa kluczowego fixed
, które zapobiega przenoszeniu przez moduł odśmiecania wskazanego obiektu w ramach zarządzania pamięcią, podczas gdy wskaźnik znajduje się w zakresie, dzięki czemu adres wskaźnika jest prawidłowy.
Wyjątkiem jest użycie struktury IntPtr
, która jest bezpiecznym zarządzanym odpowiednikiem int*
i nie wymaga niebezpiecznego kodu. Ten typ jest często zwracany podczas używania metod z System.Runtime.InteropServices
, na przykład:
Platforma .NET zawiera wiele klas i metod w System
i System.Runtime.InteropServices
przestrzenie nazw (takie jak klasa Marshal
), które konwertują typy .NET (na przykład System.String
) do i z wielu niezarządzanych typów i wskaźników (na przykład LPWSTR
lub void*
), aby zezwolić komunikacja z niezarządzanym kodem. Większość takich metod ma takie same wymagania dotyczące uprawnień bezpieczeństwa jak kod niezarządzany, ponieważ mogą wpływać na dowolne miejsca w pamięci.
COBOLEdit
Język programowania COBOL obsługuje wskaźniki do zmiennych. Prymitywne lub grupowe (rekordowe) obiekty danych zadeklarowane w LINKAGE SECTION
programu są z natury oparte na wskaźnikach, gdzie jedyną pamięcią przydzieloną w programie jest miejsce na adres elementu danych ( zazwyczaj pojedyncze słowo pamięci). W kodzie źródłowym programu te elementy danych są używane tak samo, jak każda inna zmienna WORKING-STORAGE
, ale dostęp do ich zawartości jest pośredni pośrednio za pośrednictwem wskaźników LINKAGE
.
Miejsce w pamięci dla każdego wskazanego obiektu danych jest zwykle przydzielane dynamicznie za pomocą zewnętrznych instrukcji CALL
lub za pomocą wbudowanych konstrukcji języka rozszerzonego, takich jak lub EXEC SQL
.
Rozszerzone wersje języka COBOL udostępniają również zmienne wskaźnikowe zadeklarowane za pomocą USAGE
klauzule IS
POINTER
. Wartości takich zmiennych wskaźnikowych są ustalane i modyfikowane za pomocą instrukcji SET
i SET
ADDRESS
.
Niektóre rozszerzone wersje języka COBOL udostępniają także zmienne PROCEDURE-POINTER
, które mogą przechowywać adresy wykonywalnego kodu.
PL / IEdit
Język PL / I zapewnia pełną obsługę wskaźników do wszystkich typów danych (w tym wskaźników do struktur), rekursji, wielozadaniowości, obsługi ciągów i rozbudowanych funkcji wbudowanych.PL / I był sporym krokiem naprzód w porównaniu z językami programowania tamtych czasów. Wskaźniki PL / I nie mają typu, dlatego nie jest wymagane rzutowanie do wyłuskiwania lub przypisywania wskaźników. Składnia deklaracji wskaźnika to DECLARE xxx POINTER;
, która deklaruje wskaźnik o nazwie „xxx”. Wskaźniki są używane ze zmiennymi BASED
. Zmienną opartą można zadeklarować z domyślnym lokalizatorem (DECLARE xxx BASED(ppp);
lub bez (DECLARE xxx BASED;
), gdzie xxx jest zmienną bazową, która może być zmienna elementu, struktura lub tablica, a ppp jest domyślnym wskaźnikiem). Taka zmienna może być adresowana bez wyraźnego odwołania do wskaźnika (xxx=1;
lub może być adresowana za pomocą jawnego odniesienia do domyślnego lokalizatora (ppp) lub dowolnego innego wskaźnika (qqq->xxx=1;
).
Arytmetyka wskaźników nie jest częścią standardu PL / I, ale wiele kompilatorów pozwala na wyrażenia w postaci ptr = ptr±expression
. IBM PL / I ma również wbudowaną funkcję PTRADD
do wykonywania działań arytmetycznych. Arytmetyka wskaźników jest zawsze wykonywana w bajtach.
Kompilatory IBM Enterprise PL / I mają nowa forma typowego wskaźnika o nazwie HANDLE
.
DEdit
Język programowania D jest pochodną C i C ++, który w pełni obsługuje C wskaźniki i typowanie typu C.
EiffelEdit
W języku obiektowym Eiffel stosuje się semantykę wartości i referencji bez arytmetyki wskaźnikowej. Niemniej jednak dostępne są klasy wskaźników. Oferują arytmetykę wskaźników, rzutowanie typów, jawne zarządzanie pamięcią, wew erfacing z oprogramowaniem innym niż Eiffel i innymi funkcjami.
FortranEdit
Fortran-90 wprowadził możliwość silnie wpisywanego wskaźnika. Wskaźniki w języku Fortran zawierają więcej niż zwykły adres pamięci. Hermetyzują także dolną i górną granicę wymiarów tablicy, ciągów (na przykład w celu obsługi dowolnych sekcji tablicy) i innych metadanych. Operator skojarzenia =>
służy do powiązania POINTER
ze zmienną, która ma TARGET
atrybut. Instrukcja Fortran-90 ALLOCATE
może być również użyta do skojarzenia wskaźnika z blokiem pamięci. Na przykład następujący kod może zostać użyty do zdefiniowania i utworzenia połączonej struktury listy:
Fortran-2003 dodaje obsługę wskaźników procedur. Ponadto, jako część funkcji C Interoperability, Fortran-2003 obsługuje funkcje wewnętrzne do konwersji wskaźników w stylu C na wskaźniki Fortran iz powrotem.
GoEdit
Go ma wskaźniki. Jego składnia deklaracji jest równoważna składni C, ale jest napisana na odwrót, kończąc na typie. W przeciwieństwie do C, Go ma wyrzucanie elementów bezużytecznych i nie zezwala na arytmetykę wskaźnika. Typy referencyjne, podobnie jak w C ++, nie istnieją. Niektóre typy wbudowane, takie jak mapy i kanały, są otoczone ramkami (tj. Wewnętrznie są wskaźnikami do struktur podlegających zmianom) i są inicjowane za pomocą funkcji make
. W podejściu do ujednoliconej składni między wskaźnikami i nie-wskaźnikami, operator strzałki (->
) został usunięty: operator kropki na wskaźniku odnosi się do pola lub metody wyłuskiwanego obiektu . To jednak działa tylko z 1 poziomem pośrednictwa.
JavaEdit
W przeciwieństwie do C, C ++ czy Pascal, w Javie nie ma jawnej reprezentacji wskaźników. Zamiast tego bardziej złożone struktury danych, takie jak obiekty i tablice, są implementowane przy użyciu odwołań. Język nie zapewnia żadnych jawnych operatorów manipulacji wskaźnikiem. Kod nadal może próbować wyłuskać odwołanie o wartości null (wskaźnik o wartości null), co powoduje zgłoszenie wyjątku czasu wykonywania. Miejsce zajmowane przez obiekty pamięci, do których nie istnieją odniesienia, jest automatycznie odzyskiwane przez czyszczenie pamięci w czasie wykonywania.
Modula-2Edit
Wskaźniki są implementowane podobnie jak w Pascalu, podobnie jak w wywołaniach procedur. Modula-2 jest jeszcze silniej wpisywany niż Pascal, z mniejszą liczbą sposobów na ucieczkę od systemu typów. Niektóre warianty Modula-2 (takie jak Modula-3) obejmują zbieranie śmieci.
OberonEdit
Podobnie jak w przypadku Modula-2, dostępne są wskaźniki. Nadal jest mniej sposobów na obejście systemu typów, więc Oberon i jego warianty są nadal bezpieczniejsze w odniesieniu do wskaźników niż Modula-2 lub jego warianty. Podobnie jak w przypadku Modula-3, usuwanie elementów bezużytecznych jest częścią specyfikacji języka.
PascalEdit
W przeciwieństwie do wielu języków, które zawierają wskaźniki, standardowy ISO Pascal pozwala tylko na odwoływanie się do dynamicznie utworzonych zmiennych są anonimowe i nie pozwalają im odwoływać się do standardowych zmiennych statycznych ani lokalnych. Nie ma arytmetyki wskaźników. Wskaźniki również muszą mieć skojarzony typ, a wskaźnik do jednego typu nie jest zgodny ze wskaźnikiem do innego typu (np. Wskaźnik do znaku nie jest zgodny ze wskaźnikiem do liczby całkowitej).Pomaga to wyeliminować problemy związane z bezpieczeństwem typu nieodłącznie związane z innymi implementacjami wskaźników, szczególnie tymi używanymi dla PL / I lub C. Eliminuje również niektóre zagrożenia powodowane przez wiszące wskaźniki, ale możliwość dynamicznego zwalniania przestrzeni odniesienia za pomocą standardowa procedura (która ma taki sam efekt jak funkcja biblioteki free
znaleziona w C) oznacza, że ryzyko zwisających wskaźników nie zostało całkowicie wyeliminowane.
Jednak w niektórych komercyjnych i open source’owych implementacjach kompilatorów Pascal (lub pochodnych) – takich jak Free Pascal, Turbo Pascal lub Object Pascal w Embarcadero Delphi – wskaźnik może odwoływać się do standardowych zmiennych statycznych lub lokalnych i może być rzutowane z jednego typu wskaźnika na inny. Co więcej, arytmetyka wskaźnika jest nieograniczona: dodawanie lub odejmowanie od wskaźnika przesuwa go o tę liczbę bajtów w dowolnym kierunku, ale przy użyciu Inc
lub Dec
standardowe procedury przesuwają wskaźnik o rozmiar typu danych, na który ma wskazywać. Wskaźnik bez typu znajduje się również pod nazwą Pointer
, który jest zgodny z innymi typami wskaźników.