KRÓTKIE PODSUMOWANIE
(które również umieszczę na górze):
(0) Myślenie o wskaźnikach jako adresach jest często dobrym narzędziem do nauki i często stanowi faktyczną implementację wskaźników do zwykłych typów danych.
(1) Ale w wielu, być może większości, kompilatorach wskaźniki do funkcji nie są adresami, ale są większe niż adres (zazwyczaj 2x, czasem więcej), lub w rzeczywistości są wskaźnikami do struktury w pamięci niż zawierają adresy funkcji i rzeczy takich jak stała pula.
(2) Wskaźniki do członków danych i wskaźniki do metod są często jeszcze dziwniejsze.
(3) Starszy kod x86 z problemami ze wskaźnikami FAR i NEAR
(4) Kilka przykładów, w szczególności IBM AS / 400, z bezpiecznymi „wskaźnikami tłuszczu”.
Jestem pewien, że możesz znaleźć więcej.
SZCZEGÓŁ:
UMMPPHHH !!!!! Wiele dotychczasowych odpowiedzi jest dość typowymi odpowiedziami na „programistów” - ale nie na kompilatory ani na sprzętowe. Ponieważ udaję, że jestem zaprzeczeniem sprzętowym i często pracuję z kompilatorami, pozwól mi wrzucić moje dwa centy:
W wielu, prawdopodobnie większości kompilatorach C, wskaźnikiem do danych typu T
jest w rzeczywistości adres T
.
W porządku.
Ale nawet w wielu z tych kompilatorów pewne wskaźniki NIE są adresami. Możesz to powiedzieć, patrząc na sizeof(ThePointer)
.
Na przykład wskaźniki do funkcji są czasami znacznie większe niż zwykłe adresy. Lub mogą obejmować poziom pośredni. Ten artykułzawiera jeden opis dotyczący procesora Intel Itanium, ale widziałem inne. Zwykle, aby wywołać funkcję, musisz znać nie tylko adres kodu funkcji, ale także adres stałej puli funkcji - obszar pamięci, z którego stałe są ładowane za pomocą pojedynczej instrukcji ładowania, zamiast kompilatora, który musi generować 64-bitowa stała z kilku instrukcji Load Immediate oraz Shift i OR. Tak więc zamiast jednego 64-bitowego adresu potrzebujesz 2 64-bitowych adresów. Niektóre ABI (interfejsy binarne aplikacji) przenoszą to jako 128 bitów, podczas gdy inne używają poziomu pośredniego, przy czym wskaźnik funkcji faktycznie jest adresem deskryptora funkcji, który zawiera 2 właśnie wspomniane adresy. Który jest lepszy? Zależy od twojego punktu widzenia: wydajność, rozmiar kodu, oraz niektóre problemy ze zgodnością - często kod zakłada, że wskaźnik może być rzutowany na długi lub długi długi, ale może również zakładać, że długi długi ma dokładnie 64 bity. Taki kod może nie być zgodny ze standardami, ale klienci mogą chcieć, aby działał.
Wielu z nas ma bolesne wspomnienia o starej architekturze segmentowej Intel x86, z BLISKIMI WSKAŹNIKAMI i DALSZYMI WSKAŹNIKAMI. Na szczęście są już prawie wymarłe, więc tylko krótkie podsumowanie: w 16-bitowym trybie rzeczywistym rzeczywisty adres liniowy był
LinearAddress = SegmentRegister[SegNum].base << 4 + Offset
W trybie chronionym może tak być
LinearAddress = SegmentRegister[SegNum].base + offset
wynikowy adres jest sprawdzany pod kątem limitu ustawionego w segmencie. Niektóre programy nie używały tak naprawdę standardowych deklaracji wskaźników FAR i NEAR C / C ++, ale wiele z nich właśnie powiedziało *T
--- ale były przełączniki kompilatora i linkera, więc na przykład wskaźniki kodu mogą znajdować się w pobliżu wskaźników, tylko 32-bitowe przesunięcie w stosunku do tego, co jest w rejestr CS (segment segmentu), podczas gdy wskaźnikami danych mogą być wskaźniki FAR, określające zarówno 16-bitowy numer segmentu, jak i 32-bitowe przesunięcie dla wartości 48-bitowej. Teraz obie te wielkości są z pewnością powiązane z adresem, ale ponieważ nie są one tego samego rozmiaru, który z nich jest adresem? Co więcej, segmenty zawierały również uprawnienia - tylko do odczytu, do odczytu i zapisu, wykonywalne - oprócz rzeczy związanych z rzeczywistym adresem.
Bardziej interesującym przykładem IMHO jest (lub być może była) rodzina IBM AS / 400. Ten komputer był jednym z pierwszych, którzy wdrożyli system operacyjny w C ++. Wskaźniki na tym machime zazwyczaj były dwukrotnie większe niż rzeczywisty rozmiar adresu - np. Jako ta prezentacjamówi, 128-bitowe wskaźniki, ale rzeczywiste adresy miały 48-64 bity, i znowu kilka dodatkowych informacji, co nazywa się zdolnością, która zapewniała uprawnienia, takie jak odczyt, zapis, a także limit zapobiegający przepełnieniu bufora. Tak: możesz to zrobić kompatybilnie z C / C ++ - a gdyby było to wszechobecne, chińska PLA i słowiańska mafia nie włamaliby się do tak wielu zachodnich systemów komputerowych. Ale historycznie większość programów C / C ++ zaniedbywała bezpieczeństwo wydajności. Co najciekawsze, rodzina AS400 pozwoliła systemowi operacyjnemu na tworzenie bezpiecznych wskaźników, które mogłyby być przekazywane nieuprzywilejowanemu kodowi, ale których nieuprawniony kod nie mógł sfałszować ani manipulować. Ponownie, bezpieczeństwo, i chociaż jest zgodny ze standardami, dużo niechlujny, niezgodny ze standardami kod C / C ++ nie będzie działał w tak bezpiecznym systemie. Ponownie istnieją oficjalne standardy,
Teraz zsiadam z mydła bezpieczeństwa i wspomnę o kilku innych sposobach, w których wskaźniki (różnych typów) często nie są tak naprawdę adresami: Wskaźniki do elementów danych, wskaźniki do metod funkcji składowych, a ich statyczne wersje są większe niż zwykły adres. Jak mówi ten post :
Istnieje wiele sposobów rozwiązania tego [problemy związane z dziedziczeniem pojedynczym i wielokrotnym oraz dziedziczeniem wirtualnym]. Oto, w jaki sposób kompilator Visual Studio decyduje się go obsłużyć: Wskaźnik do funkcji składowej wielokrotnie odziedziczonej klasy jest naprawdę strukturą. ”Dalej mówią:„ Rzutowanie wskaźnika funkcji może zmienić jego rozmiar! ”.
Jak zapewne możecie zgadnąć z mojego pontyfikatu dotyczącego (nie) bezpieczeństwa, brałem udział w projektach sprzętowych / programowych C / C ++, w których wskaźnik był traktowany bardziej jak zdolność niż surowy adres.
Mógłbym kontynuować, ale mam nadzieję, że wpadłeś na pomysł.
KRÓTKIE PODSUMOWANIE
(które również umieszczę na górze):
(0) myślenie o wskaźnikach jako adresach jest często dobrym narzędziem do nauki i często stanowi faktyczną implementację wskaźników do zwykłych typów danych.
(1) Ale w wielu, być może większości, kompilatorach wskaźniki do funkcji nie są adresami, ale są większe niż adres (zazwyczaj 2X, czasem więcej), lub w rzeczywistości są wskaźnikami do struktury w pamięci niż zawierają adresy funkcji i rzeczy takich jak stała pula.
(2) Wskaźniki do członków danych i wskaźniki do metod są często jeszcze dziwniejsze.
(3) Starszy kod x86 z problemami ze wskaźnikami FAR i NEAR
(4) Kilka przykładów, w szczególności IBM AS / 400, z bezpiecznymi „wskaźnikami tłuszczu”.
Jestem pewien, że możesz znaleźć więcej.