KURZE ZUSAMMENFASSUNG
(die ich auch oben einfügen werde):
(0) Zeiger als Adressen zu betrachten, ist oft ein gutes Lernwerkzeug und oft die eigentliche Implementierung für Zeiger auf gewöhnliche Datentypen.
(1) Bei vielen, vielleicht den meisten, Compilern sind Zeiger auf Funktionen keine Adressen, sondern größer als eine Adresse (normalerweise 2x, manchmal mehr) oder tatsächlich Zeiger auf eine Struktur im Speicher, die die Adressen von Funktionen und Ähnlichem enthält ein ständiger Pool.
(2) Zeiger auf Datenelemente und Zeiger auf Methoden sind oft noch seltsamer.
(3) Legacy-x86-Code mit FAR- und NEAR-Zeigerproblemen
(4) Mehrere Beispiele, insbesondere IBM AS / 400, mit sicheren "Fettzeigern".
Ich bin sicher, Sie können mehr finden.
DETAIL:
UMMPPHHH !!!!! Viele der bisherigen Antworten sind ziemlich typische "Programmer Weenie" -Antworten - aber keine Compiler Weenie oder Hardware Weenie. Da ich vorgebe, ein Hardware-Weenie zu sein und oft mit Compiler-Weenies arbeite, möchte ich meine zwei Cent einwerfen:
Bei vielen, wahrscheinlich den meisten C-Compilern ist ein Zeiger auf Daten vom Typ T
tatsächlich die Adresse von T
.
Fein.
Aber selbst auf vielen dieser Compiler sind bestimmte Zeiger KEINE Adressen. Sie können dies erkennen, indem Sie sich ansehen sizeof(ThePointer)
.
Zum Beispiel sind Zeiger auf Funktionen manchmal viel größer als gewöhnliche Adressen. Oder sie können eine Indirektionsebene beinhalten. Dieser Beitragbietet eine Beschreibung, die den Intel Itanium-Prozessor betrifft, aber ich habe andere gesehen. Um eine Funktion aufzurufen, müssen Sie normalerweise nicht nur die Adresse des Funktionscodes kennen, sondern auch die Adresse des Konstantenpools der Funktion - ein Speicherbereich, aus dem Konstanten mit einem einzelnen Ladebefehl geladen werden, anstatt dass der Compiler generieren muss eine 64-Bit-Konstante aus mehreren Load Immediate- und Shift- und OR-Anweisungen. Anstelle einer einzelnen 64-Bit-Adresse benötigen Sie also 2 64-Bit-Adressen. Einige ABIs (Application Binary Interfaces) verschieben dies als 128 Bit, während andere eine Indirektionsebene verwenden, wobei der Funktionszeiger tatsächlich die Adresse eines Funktionsdeskriptors ist, der die beiden gerade erwähnten tatsächlichen Adressen enthält. Welches ist besser? Hängt von Ihrer Sichtweise ab: Leistung, Codegröße, und einige Kompatibilitätsprobleme - häufig geht der Code davon aus, dass ein Zeiger auf eine lange oder eine lange Länge umgewandelt werden kann, kann aber auch davon ausgehen, dass die lange Länge genau 64 Bit beträgt. Ein solcher Code ist möglicherweise nicht standardkonform, aber Kunden möchten möglicherweise, dass er funktioniert.
Viele von uns haben schmerzhafte Erinnerungen an die alte segmentierte Intel x86-Architektur mit NEAR POINTERs und FAR POINTERS. Zum Glück sind diese mittlerweile fast ausgestorben, daher nur eine kurze Zusammenfassung: Im 16-Bit-Real-Modus war die tatsächliche lineare Adresse
LinearAddress = SegmentRegister[SegNum].base << 4 + Offset
Im geschützten Modus könnte dies der Fall sein
LinearAddress = SegmentRegister[SegNum].base + offset
Die resultierende Adresse wird anhand eines im Segment festgelegten Grenzwerts überprüft. Einige Programme verwendeten nicht wirklich Standard-C / C ++ FAR- und NEAR-Zeigerdeklarationen, aber viele sagten nur *T
---, aber es gab Compiler- und Linker-Schalter, so dass beispielsweise Codezeiger in der Nähe von Zeigern sein könnten, nur ein 32-Bit-Offset gegenüber dem, was sich darin befindet das CS-Register (Code Segment), während die Datenzeiger möglicherweise FAR-Zeiger sind, wobei sowohl eine 16-Bit-Segmentnummer als auch ein 32-Bit-Offset für einen 48-Bit-Wert angegeben werden. Nun, diese beiden Größen hängen sicherlich mit der Adresse zusammen, aber da sie nicht die gleiche Größe haben, welche von ihnen ist die Adresse? Darüber hinaus enthielten die Segmente neben Informationen zur tatsächlichen Adresse auch Berechtigungen - schreibgeschützt, schreibgeschützt, ausführbar.
Ein interessanteres Beispiel, IMHO, ist (oder war vielleicht) die IBM AS / 400-Familie. Dieser Computer war einer der ersten, der ein Betriebssystem in C ++ implementiert hat. Zeiger auf diese Machime waren normalerweise 2X die tatsächliche Adressgröße - z. B. wie in dieser Präsentationsagt, 128-Bit-Zeiger, aber die tatsächlichen Adressen waren 48-64 Bit, und wieder einige zusätzliche Informationen, was als Funktion bezeichnet wird, die Berechtigungen wie Lesen, Schreiben sowie eine Begrenzung zur Verhinderung eines Pufferüberlaufs bereitstellten. Ja, Sie können dies kompatibel mit C / C ++ tun - und wenn dies allgegenwärtig wäre, würden sich die chinesische PLA und die slawische Mafia nicht in so viele westliche Computersysteme hacken. In der Vergangenheit hat die meiste C / C ++ - Programmierung jedoch die Sicherheit für die Leistung vernachlässigt. Am interessantesten ist, dass die AS400-Familie es dem Betriebssystem ermöglichte, sichere Zeiger zu erstellen, die für nicht privilegierten Code vergeben werden konnten, die der nicht privilegierte Code jedoch nicht fälschen oder manipulieren konnte. Auch hier funktioniert die Sicherheit und der standardkonforme, viel schlampige, nicht standardkonforme C / C ++ - Code in einem so sicheren System nicht. Auch hier gibt es offizielle Standards,
Jetzt werde ich meine Sicherheits-Seifenkiste verlassen und einige andere Arten erwähnen, in denen Zeiger (verschiedener Typen) oft nicht wirklich Adressen sind: Zeiger auf Datenelemente, Zeiger auf Elementfunktionsmethoden und deren statische Versionen sind größer als eine gewöhnliche Adresse. Wie dieser Beitrag sagt:
Es gibt viele Möglichkeiten, dies zu lösen [Probleme im Zusammenhang mit einfacher oder mehrfacher Inheitanz und virtueller Vererbung]. Der Visual Studio-Compiler entscheidet sich dafür: Ein Zeiger auf eine Elementfunktion einer mehrfach vererbten Klasse ist wirklich eine Struktur. "Und sie sagen weiter:" Das Umwandeln eines Funktionszeigers kann seine Größe ändern! ".
Wie Sie wahrscheinlich anhand meiner Überlegungen zur (In-) Sicherheit erraten können, war ich an C / C ++ - Hardware- / Softwareprojekten beteiligt, bei denen ein Zeiger eher wie eine Funktion als wie eine Rohadresse behandelt wurde.
Ich könnte weitermachen, aber ich hoffe, Sie haben die Idee.
KURZE ZUSAMMENFASSUNG
(die ich auch oben einfügen werde):
(0) Das Betrachten von Zeigern als Adressen ist oft ein gutes Lernwerkzeug und oft die eigentliche Implementierung für Zeiger auf gewöhnliche Datentypen.
(1) Bei vielen, vielleicht den meisten, Compilern sind Zeiger auf Funktionen keine Adressen, sondern größer als eine Adresse (normalerweise 2X, manchmal mehr) oder tatsächlich Zeiger auf eine Struktur im Speicher, die die Adressen von Funktionen und Ähnlichem enthält ein ständiger Pool.
(2) Zeiger auf Datenelemente und Zeiger auf Methoden sind oft noch seltsamer.
(3) Legacy-x86-Code mit FAR- und NEAR-Zeigerproblemen
(4) Mehrere Beispiele, insbesondere IBM AS / 400, mit sicheren "Fettzeigern".
Ich bin sicher, Sie können mehr finden.