In einer seriösen Quelle über C werden nach Erörterung des &
Bedieners die folgenden Informationen gegeben :
... Es ist ein bisschen bedauerlich, dass die Terminologie [Adresse von] erhalten bleibt, weil sie diejenigen verwirrt, die nicht wissen, worum es bei Adressen geht, und diejenigen irreführt, die dies tun: Das Nachdenken über Zeiger, als wären sie Adressen, führt normalerweise zu Trauer. .
Andere Materialien, die ich gelesen habe (aus ebenso seriösen Quellen, würde ich sagen), haben Zeiger und den &
Operator immer unverfroren als Speicheradressen bezeichnet. Ich würde gerne weiter nach der Aktualität der Sache suchen, aber es ist schwierig, wenn seriöse Quellen anderer Meinung sind.
Jetzt bin ich etwas verwirrt - was genau ist dann ein Zeiger, wenn nicht eine Speicheradresse?
PS
Der Autor sagt später: ... Ich werde den Begriff "Adresse von" weiterhin verwenden, da es noch schlimmer wäre, einen anderen [Begriff] zu erfinden .
quelle
Antworten:
Der C-Standard definiert nicht, was ein Zeiger intern ist und wie er intern funktioniert. Dies ist beabsichtigt, um die Anzahl der Plattformen nicht zu begrenzen, auf denen C als kompilierte oder interpretierte Sprache implementiert werden kann.
Ein Zeigerwert kann eine Art ID oder Handle oder eine Kombination mehrerer IDs sein (sagen Sie Hallo zu x86-Segmenten und Offsets) und nicht unbedingt eine echte Speicheradresse. Diese ID kann eine beliebige Textzeichenfolge mit fester Größe sein. Nicht adressierte Darstellungen können für einen C-Interpreter besonders nützlich sein.
quelle
Ich bin mir über Ihre Quelle nicht sicher, aber die Art der Sprache, die Sie beschreiben, stammt aus dem C-Standard:
Also ... ja, Zeiger zeigen auf Speicheradressen. Zumindest schlägt der C-Standard dies so vor.
Um es etwas klarer auszudrücken, ein Zeiger ist eine Variable, die den Wert einer Adresse enthält . Die Adresse eines Objekts (das in einem Zeiger gespeichert sein kann) wird mit dem unären
&
Operator zurückgegeben.Ich kann die Adresse "42 Wallaby Way, Sydney" in einer Variablen speichern (und diese Variable wäre eine Art "Zeiger", aber da dies keine Speicheradresse ist, würden wir sie nicht richtig als "Zeiger" bezeichnen). Ihr Computer verfügt über Adressen für seine Speichereimer. Zeiger speichern den Wert einer Adresse (dh ein Zeiger speichert den Wert "42 Wallaby Way, Sydney", bei dem es sich um eine Adresse handelt).
Bearbeiten: Ich möchte den Kommentar von Alexey Frunze erweitern.
Was genau ist ein Zeiger? Schauen wir uns den C-Standard an:
Im Wesentlichen speichern Zeiger einen Wert, der einen Verweis auf ein Objekt oder eine Funktion bereitstellt. So'ne Art. Zeiger sollen einen Wert speichern, der auf ein Objekt oder eine Funktion verweist. Dies ist jedoch nicht immer der Fall:
Das obige Zitat besagt, dass wir eine ganze Zahl in einen Zeiger verwandeln können. Wenn wir dies tun (dh wenn wir einen ganzzahligen Wert in einen Zeiger anstelle einer bestimmten Referenz auf ein Objekt oder eine Funktion einfügen), zeigt der Zeiger möglicherweise nicht auf eine Entität vom Referenztyp (dh er liefert möglicherweise keine Verweis auf ein Objekt oder eine Funktion). Es könnte uns etwas anderes bieten. Und dies ist eine Stelle, an der Sie möglicherweise eine Art Handle oder ID in einen Zeiger stecken (dh der Zeiger zeigt nicht auf ein Objekt; er speichert einen Wert, der etwas darstellt, aber dieser Wert ist möglicherweise keine Adresse).
Also ja, wie Alexey Frunze sagt, ist es möglich, dass ein Zeiger keine Adresse für ein Objekt oder eine Funktion speichert. Es ist möglich, dass ein Zeiger stattdessen eine Art "Handle" oder ID speichert. Sie können dies tun, indem Sie einem Zeiger einen beliebigen ganzzahligen Wert zuweisen. Was dieses Handle oder diese ID darstellt, hängt vom System / der Umgebung / dem Kontext ab. Solange Ihr System / Ihre Implementierung den Wert verstehen kann, sind Sie in guter Verfassung (dies hängt jedoch vom spezifischen Wert und dem spezifischen System / der Implementierung ab).
Normalerweise speichert ein Zeiger eine Adresse für ein Objekt oder eine Funktion. Wenn keine tatsächliche Adresse (für ein Objekt oder eine Funktion) gespeichert wird, ist das Ergebnis implementierungsdefiniert (was bedeutet, dass genau das, was passiert und was der Zeiger jetzt darstellt, von Ihrem System und Ihrer Implementierung abhängt, sodass es sich möglicherweise um ein Handle oder eine ID handelt ein bestimmtes System, aber die Verwendung des gleichen Codes / Werts auf einem anderen System kann Ihr Programm zum Absturz bringen).
Das war länger als ich dachte ...
quelle
In diesem Bild,
pointer_p ist ein Zeiger, der sich bei 0x12345 befindet und auf eine Variable variable_v bei 0x34567 zeigt.
quelle
Sich einen Zeiger als Adresse vorzustellen, ist eine Annäherung . Wie alle Annäherungen ist es gut genug, um manchmal nützlich zu sein, aber es ist auch nicht genau, was bedeutet, dass das Verlassen darauf Probleme verursacht.
Ein Zeiger ist insofern wie eine Adresse, als er angibt, wo sich ein Objekt befindet. Eine unmittelbare Einschränkung dieser Analogie besteht darin, dass nicht alle Zeiger tatsächlich eine Adresse enthalten.
NULL
ist ein Zeiger, der keine Adresse ist. Der Inhalt einer Zeigervariable kann tatsächlich eine von drei Arten sein:p
die Adresse von enthält,x
hat der Ausdruck*p
den gleichen Wert wiex
);NULL
ein Beispiel ist;p
verweist (wenn er keinen gültigen Wert enthält,*p
kann er alles tun („undefiniertes Verhalten“), wobei ein Absturz des Programms häufig vorkommt).Darüber hinaus wäre es genauer zu sagen, dass ein Zeiger (falls gültig und nicht null) eine Adresse enthält : Ein Zeiger gibt an, wo ein Objekt zu finden ist, aber es sind mehr Informationen damit verbunden.
Insbesondere hat ein Zeiger einen Typ. Auf den meisten Plattformen hat der Typ des Zeigers zur Laufzeit keinen Einfluss, aber einen Einfluss, der über den Typ zur Kompilierungszeit hinausgeht. Wenn
p
es sich um einen Zeiger aufint
(int *p;
) handelt,p + 1
zeigt es auf eine Ganzzahl, nach dersizeof(int)
Bytes stehenp
(vorausgesetzt, esp + 1
handelt sich immer noch um einen gültigen Zeiger). Wennq
ein Zeigerchar
darauf auf dieselbe Adresse wiep
(char *q = p;
) verweist ,q + 1
ist er nicht dieselbe Adresse wiep + 1
. Wenn Sie sich Zeiger als Adressen vorstellen, ist es nicht sehr intuitiv, dass die „nächste Adresse“ für verschiedene Zeiger auf dieselbe Position unterschiedlich ist.In einigen Umgebungen ist es möglich, mehrere Zeigerwerte mit unterschiedlichen Darstellungen (unterschiedliche Bitmuster im Speicher) zu haben, die auf dieselbe Stelle im Speicher verweisen. Sie können sich diese als unterschiedliche Zeiger vorstellen, die dieselbe Adresse enthalten, oder als unterschiedliche Adressen für denselben Ort - die Metapher ist in diesem Fall nicht klar. Der
==
Betreiber informiert Sie immer , ob die beiden Operanden an die gleiche Stelle gerichtet sind, so auf diesen Umgebungen Sie können ,p == q
obwohlp
undq
verschiedene Bitmuster haben.Es gibt sogar Umgebungen, in denen Zeiger andere Informationen als die Adresse enthalten, z. B. Typ- oder Berechtigungsinformationen. Sie können leicht durch Ihr Leben als Programmierer gehen, ohne auf diese zu stoßen.
Es gibt Umgebungen, in denen verschiedene Arten von Zeigern unterschiedliche Darstellungen haben. Sie können sich das als verschiedene Arten von Adressen mit unterschiedlichen Darstellungen vorstellen. Beispielsweise haben einige Architekturen Byte- und Wortzeiger oder Objektzeiger und Funktionszeiger.
Alles in allem ist es nicht schlecht, Zeiger als Adressen zu betrachten, solange Sie dies berücksichtigen
Umgekehrt ist es weitaus schwieriger. Nicht alles, was wie eine Adresse aussieht, kann ein Zeiger sein . Irgendwo in der Tiefe wird jeder Zeiger als Bitmuster dargestellt, das als Ganzzahl gelesen werden kann, und Sie können sagen, dass diese Ganzzahl eine Adresse ist. Aber in die andere Richtung ist nicht jede ganze Zahl ein Zeiger.
Es gibt zunächst einige bekannte Einschränkungen; Beispielsweise kann eine Ganzzahl, die einen Ort außerhalb des Adressraums Ihres Programms angibt, kein gültiger Zeiger sein. Eine falsch ausgerichtete Adresse ist kein gültiger Zeiger für einen Datentyp, der ausgerichtet werden muss. Auf einer Plattform, auf der eine
int
4-Byte-Ausrichtung erforderlich ist, kann 0x7654321 beispielsweise kein gültigerint*
Wert sein.Es geht jedoch weit darüber hinaus, denn wenn Sie einen Zeiger auf eine Ganzzahl setzen, werden Sie in eine Welt voller Probleme geraten. Ein großer Teil dieses Problems besteht darin, dass die Optimierung von Compilern bei der Mikrooptimierung weitaus besser ist als von den meisten Programmierern erwartet, so dass ihr mentales Modell der Funktionsweise eines Programms zutiefst falsch ist. Nur weil Sie Zeiger mit derselben Adresse haben, heißt das nicht, dass sie gleichwertig sind. Betrachten Sie beispielsweise das folgende Snippet:
Sie können erwarten, dass auf einer normalen Maschine, auf der
sizeof(int)==4
undsizeof(short)==2
, entweder1 = 1?
(Little-Endian) oder65536 = 1?
(Big-Endian) gedruckt wird . Aber auf meinem 64-Bit-Linux-PC mit GCC 4.4:GCC ist so freundlich, uns zu warnen, was in diesem einfachen Beispiel falsch läuft - in komplexeren Beispielen bemerkt der Compiler dies möglicherweise nicht. Da
p
hat einen anderen Typ als das&x
Ändern derp
Punkte, auf die die&x
Punkte nicht beeinflusst werden sollen (außerhalb einiger genau definierter Ausnahmen). Daher steht es dem Compiler frei, den Wert vonx
in einem Register zu behalten und dieses Register nicht als*p
Änderungen zu aktualisieren . Das Programm dereferenziert zwei Zeiger auf dieselbe Adresse und erhält zwei unterschiedliche Werte!Die Moral dieses Beispiels ist, dass es in Ordnung ist, sich einen (nicht null gültigen) Zeiger als Adresse vorzustellen, solange Sie die genauen Regeln der C-Sprache einhalten. Die Kehrseite der Medaille ist, dass die Regeln der C-Sprache kompliziert sind und es schwierig ist, ein intuitives Gefühl dafür zu bekommen, wenn Sie nicht wissen, was unter der Haube passiert. Und was unter der Haube passiert, ist, dass die Verbindung zwischen Zeigern und Adressen etwas locker ist, sowohl um „exotische“ Prozessorarchitekturen zu unterstützen als auch um die Optimierung von Compilern zu unterstützen.
Stellen Sie sich Zeiger als einen ersten Schritt in Ihrem Verständnis vor, aber folgen Sie dieser Intuition nicht zu weit.
quelle
*p = 3
garantiert erfolgreich ist, wenn p nicht initialisiert wurde.NULL
ist nicht, aber für den hier erforderlichen Detaillierungsgrad ist dies eine irrelevante Ablenkung. Selbst für die tägliche Programmierung kommt die Tatsache, dassNULL
sie als etwas implementiert werden kann, das nicht „Zeiger“ sagt, nicht oft vor (hauptsächlichNULL
in Richtung einer variadischen Funktion - aber auch dort, wenn Sie sie nicht umsetzen Sie gehen bereits davon aus, dass alle Zeigertypen dieselbe Darstellung haben.Ein Zeiger ist eine Variable, die die Speicheradresse HÄLT, nicht die Adresse selbst. Sie können jedoch einen Zeiger dereferenzieren - und Zugriff auf den Speicherort erhalten.
Beispielsweise:
Das ist es. So einfach ist das.
Ein Programm, das zeigt, was ich sage und was es ausgibt:
http://ideone.com/rcSUsb
Das Programm:
quelle
fopen
in einer Variablen speichern , wenn Sie es mehrmals verwenden müssen (wasfopen
fast immer der Fall ist).Es ist schwierig, genau zu sagen, was die Autoren dieser Bücher genau meinen. Ob ein Zeiger eine Adresse enthält oder nicht, hängt davon ab, wie Sie eine Adresse definieren und wie Sie einen Zeiger definieren.
Nach all den Antworten zu urteilen, die geschrieben wurden, gehen einige Leute davon aus, dass (1) eine Adresse eine ganze Zahl sein muss und (2) ein Zeiger nicht virtuell sein muss, um dies in der Spezifikation nicht zu sagen. Mit diesen Annahmen enthalten Zeiger dann eindeutig nicht unbedingt Adressen.
Wir sehen jedoch, dass (2) wahrscheinlich wahr ist, (1) wahrscheinlich nicht wahr sein muss. Und was ist mit der Tatsache zu tun , dass das & gemäß der Antwort von @ CornStalks als Adresse des Betreibers bezeichnet wird? Bedeutet dies, dass die Autoren der Spezifikation beabsichtigen, dass ein Zeiger eine Adresse enthält?
Können wir also sagen, der Zeiger enthält eine Adresse, aber eine Adresse muss keine Ganzzahl sein? Vielleicht.
Ich denke, das alles ist jibberisches pedantisches semantisches Gerede. Es ist praktisch völlig wertlos. Können Sie sich einen Compiler vorstellen, der Code so generiert, dass der Wert eines Zeigers keine Adresse ist? Wenn ja, was? Das ist was ich dachte...
Ich denke, der Autor des Buches (der erste Auszug, der behauptet, dass Zeiger nicht unbedingt nur Adressen sind) bezieht sich wahrscheinlich auf die Tatsache, dass ein Zeiger die inhärenten Typinformationen enthält.
Beispielsweise,
sowohl y als auch z sind Zeiger, aber y + 1 und z + 1 sind unterschiedlich. Wenn es sich um Speicheradressen handelt, geben Ihnen diese Ausdrücke dann nicht den gleichen Wert?
Und hier in Lügen führt das Denken über Zeiger, als wären sie Adressen, normalerweise zu Trauer . Es wurden Fehler geschrieben, weil die Leute über Zeiger nachdenken, als wären sie Adressen , und dies führt normalerweise zu Trauer .
55555 ist wahrscheinlich kein Zeiger, obwohl es eine Adresse sein kann, aber (int *) 55555 ist ein Zeiger. 55555 + 1 = 55556, aber (int *) 55555 + 1 ist 55559 (+/- Unterschied in Bezug auf die Größe von (int)).
quelle
far
Zeiger ist nicht nur "eine ganze Zahl".Nun, ein Zeiger ist eine Abstraktion, die einen Speicherort darstellt. Beachten Sie, dass das Zitat nicht besagt, dass es falsch ist, an Zeiger zu denken, als wären sie Speicheradressen, sondern nur, dass dies "normalerweise zu Trauer führt". Mit anderen Worten, es führt zu falschen Erwartungen.
Die wahrscheinlichste Quelle der Trauer ist sicherlich die Zeigerarithmetik, die tatsächlich eine der Stärken von C ist. Wenn ein Zeiger eine Adresse wäre, würde man erwarten, dass die Zeigerarithmetik eine Adressarithmetik ist. aber es ist nicht. Wenn Sie beispielsweise einer Adresse 10 hinzufügen, erhalten Sie eine Adresse, die um 10 Adressierungseinheiten größer ist. Durch Hinzufügen von 10 zu einem Zeiger wird dieser jedoch um das 10-fache der Art des Objekts erhöht, auf das er zeigt (und nicht einmal die tatsächliche Größe, sondern auf eine Ausrichtungsgrenze aufgerundet). Bei einer
int *
normalen Architektur mit 32-Bit-Ganzzahlen würde das Hinzufügen von 10 zu 40 Adressierungseinheiten (Bytes) inkrementieren. Erfahrene C-Programmierer sind sich dessen bewusst und leben damit, aber Ihr Autor ist offensichtlich kein Fan von schlampigen Metaphern.Es gibt die zusätzliche Frage, wie der Inhalt des Zeigers repräsentiert den Speicherplatz: Wie viele Antworten erklärt hat, eine Adresse ist nicht immer ein int (oder lang). In einigen Architekturen ist eine Adresse ein "Segment" plus ein Offset. Ein Zeiger kann sogar nur den Versatz in das aktuelle Segment enthalten ("nahe" Zeiger), der für sich genommen keine eindeutige Speicheradresse ist. Und der Zeigerinhalt hat möglicherweise nur eine indirekte Beziehung zu einer Speicheradresse, wie die Hardware sie versteht. Aber der Autor des zitierten Zitats erwähnt nicht einmal die Repräsentation, daher denke ich, dass sie eher an konzeptionelle Äquivalenz als an Repräsentation gedacht hatten.
quelle
So habe ich es in der Vergangenheit einigen verwirrten Personen erklärt: Ein Zeiger hat zwei Attribute, die sich auf sein Verhalten auswirken. Es hat einen Wert , der (in typischen Umgebungen) eine Speicheradresse ist, und einen Typ , der den Typ und die Größe des Objekts angibt , auf das es zeigt.
Zum Beispiel gegeben:
Sie können drei verschiedene Zeiger haben, die alle auf dasselbe Objekt zeigen:
Wenn Sie die Werte dieser Zeiger vergleichen, sind sie alle gleich:
Wenn Sie jedoch jeden Zeiger inkrementieren, werden Sie feststellen, dass der Typ , auf den sie zeigen, relevant wird.
Die Variablen
i
undc
werden an diesem Punkt verschiedene Werte haben, weili++
Ursachen ,i
die Adresse der nächsten zugängliche ganze Zahl zu enthalten, undc++
führenc
zu Punkt zum nächsten adressierbaren Charakter. In der Regel belegen Ganzzahlen mehr Speicher als Zeichen und haben daheri
einen größeren Wert alsc
nach dem Inkrementieren beider Zeichen .quelle
i == c
ist schlecht geformt (Sie können Zeiger nur dann mit verschiedenen Typen vergleichen, wenn eine implizite Konvertierung von einem zum anderen erfolgt). Wenn Sie dies mit einer Besetzung beheben, bedeutet dies, dass Sie eine Konvertierung angewendet haben. Anschließend ist fraglich, ob die Konvertierung den Wert ändert oder nicht. (Sie könnten behaupten, dass dies nicht der Fall ist, aber das ist nur das Gleiche, was Sie mit diesem Beispiel beweisen wollten).Mark Bessey hat es bereits gesagt, aber dies muss noch einmal betont werden, bis es verstanden wird.
Zeiger hat genauso viel mit einer Variablen zu tun wie mit einem Literal 3.
Der Zeiger ist ein Tupel aus einem Wert (einer Adresse) und einem Typ (mit zusätzlichen Eigenschaften, z. B. schreibgeschützt). Der Typ (und gegebenenfalls die zusätzlichen Parameter) können den Kontext weiter definieren oder einschränken. z.B.
__far ptr, __near ptr
: Was ist der Kontext der Adresse: Stapel, Heap, lineare Adresse, Versatz von irgendwoher, physischer Speicher oder was.Es ist die Eigenschaft vom Typ , die die Zeigerarithmetik ein wenig von der Ganzzahlarithmetik unterscheidet.
Die Zählerbeispiele für einen Zeiger, der keine Variable ist, sind zu viele, um sie zu ignorieren
fopen gibt einen FILE-Zeiger zurück. (Wo ist die Variable?)
Stapelzeiger oder Rahmenzeiger sind typischerweise nicht adressierbare Register
*(int *)0x1231330 = 13;
- Umwandlung eines beliebigen Ganzzahlwerts in einen Zeiger vom Typ "Ganzzahl" und Schreiben / Lesen einer Ganzzahl, ohne jemals eine Variable einzuführenWährend der Lebensdauer eines C-Programms gibt es viele andere Instanzen von temporären Zeigern, die keine Adressen haben - und daher keine Variablen, sondern Ausdrücke / Werte mit einem der Kompilierungszeit zugeordneten Typ.
quelle
Du hast recht und bist gesund. Normalerweise ist ein Zeiger nur eine Adresse, sodass Sie ihn in eine Ganzzahl umwandeln und beliebige Arithmetiken ausführen können.
Aber manchmal sind Zeiger nur ein Teil einer Adresse. Bei einigen Architekturen wird ein Zeiger in eine Adresse mit zusätzlicher Basis konvertiert oder ein anderes CPU- Register verwendet.
Heutzutage ist es auf einer PC- und ARM- Architektur mit einem flachen Speichermodell und einer C-Sprache, die nativ kompiliert wurde, in Ordnung zu glauben, dass ein Zeiger eine ganzzahlige Adresse zu einem Ort im eindimensional adressierbaren RAM ist.
quelle
Ein Zeiger ist wie jede andere Variable in C im Grunde eine Sammlung von Bits, die durch einen oder mehrere verkettete
unsigned char
Werte dargestellt werden können (wie bei jeder anderen Art von Cariable,sizeof(some_variable)
gibt die Anzahl derunsigned char
Werte an). Was einen Zeiger von anderen Variablen unterscheidet, ist, dass ein C-Compiler die Bits in einem Zeiger so interpretiert, dass er irgendwie einen Ort identifiziert, an dem eine Variable gespeichert werden kann. In C ist es im Gegensatz zu einigen anderen Sprachen möglich, Speicherplatz für mehrere Variablen anzufordern und dann einen Zeiger auf einen beliebigen Wert in dieser Menge in einen Zeiger auf eine andere Variable in dieser Menge umzuwandeln.Viele Compiler implementieren Zeiger, indem sie ihre Bits verwenden, um die tatsächlichen Maschinenadressen zu speichern. Dies ist jedoch nicht die einzig mögliche Implementierung. Eine Implementierung könnte ein Array - für den Benutzercode nicht zugänglich - behalten, das die Hardwareadresse und die zugewiesene Größe aller von einem Programm verwendeten Speicherobjekte (Sätze von Variablen) auflistet, und jeden Zeiger einen Index in einem Array enthalten lassen mit einem Versatz von diesem Index. Ein solches Design würde es einem System ermöglichen, den Code nicht nur darauf zu beschränken, nur mit dem Speicher zu arbeiten, den es besitzt, sondern auch sicherzustellen, dass ein Zeiger auf ein Speicherelement nicht versehentlich in einen Zeiger auf ein anderes Speicherelement konvertiert werden kann (in einem System, das Hardware verwendet Adressen, wenn
foo
undbar
Arrays von 10 Elementen sind, die nacheinander im Speicher gespeichert sind, ein Zeiger auf das "elfte" Element vonfoo
könnte stattdessen auf das erste Element von zeigenbar
, aber in einem System, in dem jeder "Zeiger" eine Objekt-ID und ein Offset ist, könnte das System abfangen, wenn Code versucht, einen Zeiger auf einen Wertfoo
außerhalb seines zugewiesenen Bereichs zu indizieren . Es wäre für ein solches System auch möglich, Speicherfragmentierungsprobleme zu beseitigen, da die physischen Adressen, die Zeigern zugeordnet sind, verschoben werden könnten.Beachten Sie, dass Zeiger zwar etwas abstrakt sind, aber nicht abstrakt genug, um es einem vollständig standardkonformen C-Compiler zu ermöglichen, einen Garbage Collector zu implementieren. Der C-Compiler gibt an, dass jede Variable, einschließlich Zeiger, als Folge von
unsigned char
Werten dargestellt wird. Wenn eine Variable gegeben ist, kann man sie in eine Folge von Zahlen zerlegen und diese Folge später wieder in eine Variable des ursprünglichen Typs umwandeln. Folglich wäre es für ein Programm möglichcalloc
Einige Speicher (die einen Zeiger darauf erhalten), speichern dort etwas, zerlegen den Zeiger in eine Reihe von Bytes, zeigen diese auf dem Bildschirm an und löschen dann alle Verweise darauf. Wenn das Programm dann einige Zahlen von der Tastatur akzeptierte, diese zu einem Zeiger rekonstituierte und dann versuchte, Daten von diesem Zeiger zu lesen, und wenn der Benutzer dieselben Zahlen eingab, die das Programm zuvor angezeigt hatte, musste das Programm die Daten ausgeben das war imcalloc
ed-Speicher gespeichert worden . Da es keine denkbare Möglichkeit gibt, dass der Computer wissen könnte, ob der Benutzer eine Kopie der angezeigten Nummern erstellt hat, wäre es nicht denkbar, dass der Computer wissen könnte, ob in Zukunft jemals auf den oben genannten Speicher zugegriffen werden könnte.quelle
free
sei denn, dies wird natürlich explizit aufgerufen). Ob die resultierende Implementierung allzu nützlich wäre, ist eine andere Frage, da ihre Fähigkeit zum Sammeln möglicherweise zu eingeschränkt ist, aber Sie könnten es zumindest als Garbage Collector bezeichnen :-) Zeigerzuweisung und Arithmetik würden den Wert nicht "verlieren", aber Jeder Zugang zu einemchar*
unbekannten Ursprung müsste überprüft werden.free
nicht aufgerufen wurde, zuverlässig freizugeben , oder verhindern, dass ein Verweis auf ein freigegebenes Objekt zu einem Verweis auf ein lebendes Objekt wird [selbst wenn Ressourcen verwendet werden, die dies erfordern explizite Lebensdauerverwaltung, GC kann die letztere Funktion weiterhin sinnvoll ausführen]; Ein GC-System, das Objekte manchmal fälschlicherweise als Live-Referenzen betrachtet, kann verwendet werden, wenn die Wahrscheinlichkeit, dass N Objekte unnötig gleichzeitig fixiert werden, gegen Null geht, wenn N groß wird . Es sei denn, man ist bereit, einen Compilerfehler zuEin Zeiger ist ein Variablentyp, der in C / C ++ nativ verfügbar ist und eine Speicheradresse enthält. Wie jede andere Variable hat sie eine eigene Adresse und belegt Speicher (die Menge ist plattformspezifisch).
Ein Problem, das Sie als Ergebnis der Verwirrung sehen werden, ist der Versuch, den Referenten innerhalb einer Funktion zu ändern, indem Sie einfach den Zeiger als Wert übergeben. Dadurch wird eine Kopie des Zeigers im Funktionsbereich erstellt, und alle Änderungen an der Stelle, an der dieser neue Zeiger "zeigt", ändern nicht den Verweis des Zeigers auf den Bereich, der die Funktion aufgerufen hat. Um den tatsächlichen Zeiger innerhalb einer Funktion zu ändern, würde man normalerweise einen Zeiger an einen Zeiger übergeben.
quelle
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 vonT
.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
Im geschützten Modus könnte dies der Fall sein
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:
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.
quelle
LinearAddress = SegmentRegister.Selector * 16 + Offset
(Notenzeiten 16, nicht um 16 verschieben). Im geschützten ModusLinearAddress = SegmentRegister.base + offset
(keine Multiplikation jeder Art, die Segment - Basis ist in dem GDT / LDT und zwischengespeichert in dem Segmentregister gespeichert wie ).Ein Zeiger ist nur eine andere Variable, die verwendet wird, um die Adresse eines Speicherorts zu speichern (normalerweise die Speicheradresse einer anderen Variablen).
quelle
Sie können es so sehen. Ein Zeiger ist ein Wert, der eine Adresse im adressierbaren Speicherbereich darstellt.
quelle
Ein Zeiger ist nur eine andere Variable, die normalerweise die Speicheradresse einer anderen Variablen enthalten kann. Ein Zeiger, der eine Variable ist, hat auch eine Speicheradresse.
quelle
Der Wechselstromzeiger ist einer Speicheradresse sehr ähnlich, jedoch mit weg abstrahierten maschinenabhängigen Details sowie einigen Funktionen, die im Befehlssatz der unteren Ebene nicht enthalten sind.
Zum Beispiel ist ein C-Zeiger relativ reich typisiert. Wenn Sie einen Zeiger durch ein Array von Strukturen inkrementieren, springt er gut von einer Struktur zur anderen.
Zeiger unterliegen Konvertierungsregeln und ermöglichen die Überprüfung des Typs der Kompilierungszeit.
Es gibt einen speziellen "Nullzeiger" -Wert, der auf Quellcodeebene portierbar ist, dessen Darstellung jedoch unterschiedlich sein kann. Wenn Sie einem Zeiger eine Ganzzahlkonstante zuweisen, deren Wert Null ist, nimmt dieser Zeiger den Nullzeigerwert an. Das Gleiche gilt, wenn Sie einen Zeiger auf diese Weise initialisieren.
Ein Zeiger kann als boolesche Variable verwendet werden: Er testet true, wenn er nicht null ist, und false, wenn er null ist.
Wenn der Nullzeiger in einer Maschinensprache eine lustige Adresse wie 0xFFFFFFFF ist, müssen Sie möglicherweise explizite Tests für diesen Wert durchführen. C versteckt das vor dir. Auch wenn der Nullzeiger 0xFFFFFFFF ist, können Sie ihn mit testen
if (ptr != 0) { /* not null! */}
.Die Verwendung von Zeigern, die das Typsystem untergraben, führt zu undefiniertem Verhalten, während ähnlicher Code in der Maschinensprache möglicherweise gut definiert ist. Assembler stellen die von Ihnen geschriebenen Anweisungen zusammen, C-Compiler optimieren jedoch unter der Annahme, dass Sie nichts falsch gemacht haben. Wenn ein
float *p
Zeiger auf einelong n
Variable zeigt und*p = 0.0
ausgeführt wird, muss der Compiler dies nicht verarbeiten. Eine nachfolgende Verwendung vonn
wird das Bitmuster des Float-Werts nicht unbedingt lesen, aber möglicherweise handelt es sich um einen optimierten Zugriff, der auf der Annahme des "strengen Aliasing" basiert,n
die nicht berührt wurde! Das heißt, die Annahme, dass sich das Programm gut verhält und daherp
nicht darauf hinweisen sollten
.In C sind Zeiger auf Code und Zeiger auf Daten unterschiedlich, aber auf vielen Architekturen sind die Adressen gleich. Es können C-Compiler entwickelt werden, die "fette" Zeiger haben, obwohl die Zielarchitektur dies nicht tut. Fette Zeiger bedeuten, dass Zeiger nicht nur Maschinenadressen sind, sondern auch andere Informationen enthalten, z. B. Informationen über die Größe des Objekts, auf das gezeigt wird, um die Grenzen zu überprüfen. Portabel geschriebene Programme lassen sich leicht auf solche Compiler portieren.
Sie sehen also, dass es viele semantische Unterschiede zwischen Maschinenadressen und C-Zeigern gibt.
quelle
ptr != 0
es sich nicht um einen Nulltest handelt, geben Sie bitte dessen Identität an (aber senden Sie vorher einen Fehlerbericht an den Anbieter).Bevor wir Zeiger verstehen, müssen wir Objekte verstehen. Objekte sind Entitäten, die existieren und einen Standortbezeichner haben, der als Adresse bezeichnet wird. Ein Zeiger ist nur eine Variable wie alle anderen Variablen
C
mit einem Typ namens,pointer
dessen Inhalt als Adresse eines Objekts interpretiert wird, das die folgende Operation unterstützt.Ein Zeiger wird basierend auf dem Objekttyp klassifiziert, auf den er sich gerade bezieht. Der einzige Teil der Informationen, auf die es ankommt, ist die Größe des Objekts.
Jedes Objekt unterstützt eine Operation
&
(Adresse von), die den Standortbezeichner (Adresse) des Objekts als Zeigerobjekttyp abruft. Dies sollte die Verwirrung um die Nomenklatur verringern, da dies sinnvoll wäre,&
als Operation eines Objekts und nicht als Zeiger aufzurufen, dessen resultierender Typ ein Zeiger des Objekttyps ist.Hinweis In dieser Erklärung habe ich das Konzept des Gedächtnisses weggelassen.
quelle
&
als "Adresse von" verursachen, da dies eher an ein Objekt als an den Zeiger an sich gebunden ist "Eine Adresse wird verwendet, um ein Stück Speicher mit fester Größe, normalerweise für jedes Byte, als Ganzzahl zu identifizieren. Dies wird genau als Byteadresse bezeichnet , die auch von der ISO C verwendet wird. Es kann einige andere Methoden geben, um eine Adresse zu erstellen, z. B. für jedes Bit. Es wird jedoch so oft nur die Byteadresse verwendet, dass wir normalerweise "Byte" weglassen.
Technisch gesehen ist eine Adresse niemals ein Wert in C, da die Definition des Begriffs "Wert" in (ISO) C lautet:
(Von mir hervorgehoben.) Es gibt jedoch keinen solchen "Adresstyp" in C.
Zeiger ist nicht dasselbe. Zeiger ist eine Art Typ in der C-Sprache. Es gibt verschiedene Zeigertypen. Sie führt nicht zwangsläufig zu identischem Satz von Regeln der Sprache gehorcht, zum Beispiel der Wirkung
++
auf einem Wert vom Typint*
vs.char*
.Ein Wert in C kann vom Zeigertyp sein. Dies wird als Zeigerwert bezeichnet . Ein Zeigerwert ist in der C-Sprache kein Zeiger. Wir sind es jedoch gewohnt, sie miteinander zu mischen, da es in C wahrscheinlich nicht mehrdeutig ist: Wenn wir einen Ausdruck
p
als "Zeiger" bezeichnen, ist er lediglich ein Zeigerwert, aber kein Typ, da ein benannter Typ in C dies nicht ist durch einen ausgedrückt Ausdruck , sondern durch eine Typname oder ein typedef-Namen .Einige andere Dinge sind subtil. Als C-Benutzer sollte man zunächst wissen, was
object
bedeutet:Ein Objekt ist eine Entität zur Darstellung von Werten eines bestimmten Typs. Ein Zeiger ist ein Objekttyp . Wenn wir also deklarieren
int* p;
,p
bedeutet dies "ein Objekt vom Zeigertyp" oder ein "Zeigerobjekt".Beachten Sie, dass es keine "Variable" gibt, die normativ durch den Standard definiert ist (tatsächlich wird sie von ISO C im normativen Text niemals als Substantiv verwendet). Informell nennen wir ein Objekt jedoch eine Variable, wie es eine andere Sprache tut. (Aber immer noch nicht so genau, z. B. in C ++ kann eine Variable normativ vom Referenztyp sein , was kein Objekt ist.) Die Ausdrücke "Zeigerobjekt" oder "Zeigervariable" werden manchmal wie oben als "Zeigerwert" behandelt, mit a wahrscheinlich geringfügiger Unterschied. (Eine weitere Reihe von Beispielen ist "Array".)
Da der Zeiger ein Typ ist und die Adresse in C effektiv "typenlos" ist, "enthält" ein Zeigerwert ungefähr eine Adresse. Und ein Ausdruck des Zeigertyps kann ergeben eine Adresse, zB
ISO C11 6.5.2.3
Beachten Sie, dass dieser Wortlaut durch WG14 / N1256, dh ISO C99: TC3, eingeführt wird. In C99 gibt es
Es spiegelt die Meinung des Ausschusses wider: Eine Adresse ist kein Zeigerwert, der vom unären
&
Operator zurückgegeben wird.Trotz des obigen Wortlauts gibt es auch in den Standards noch einige Unordnung.
ISO C11 6.6
ISO C ++ 11 5.19
(Der aktuelle C ++ - Standardentwurf verwendet einen anderen Wortlaut, sodass dieses Problem nicht auftritt.)
Tatsächlich sind sowohl "Adresskonstante" in C als auch "Adresskonstantenausdruck" in C ++ konstante Ausdrücke von Zeigertypen (oder zumindest "zeigerähnliche" Typen seit C ++ 11).
Und der eingebaute unäre
&
Operator wird in C und C ++ als "Adresse von" bezeichnet. ähnlichstd::addressof
wird in C ++ 11 eingeführt.Diese Benennung kann zu Missverständnissen führen. Der resultierende Ausdruck ist von Zeigertyp, so dass sie als interpretiert werden würde: Das Ergebnis enthält / ergibt sich eine Adresse, anstatt ist eine Adresse.
quelle
Es heißt "weil es diejenigen verwirrt, die nicht wissen, worum es bei Adressen geht" - es ist auch wahr: Wenn Sie erfahren, worum es bei Adressen geht, werden Sie nicht verwirrt sein. Theoretisch ist der Zeiger eine Variable, die auf eine andere zeigt und praktisch eine Adresse enthält, die die Adresse der Variablen ist, auf die er zeigt. Ich weiß nicht, warum ich diese Tatsache verbergen sollte , es ist keine Raketenwissenschaft. Wenn Sie Zeiger verstehen, sind Sie der Funktionsweise von Computern einen Schritt näher gekommen. Gehen Sie geradeaus!
quelle
Kommen Sie, um darüber nachzudenken, ich denke, es ist eine Frage der Semantik. Ich denke nicht, dass der Autor Recht hat, da der C-Standard einen Zeiger als eine Adresse für das referenzierte Objekt bezeichnet, wie andere hier bereits erwähnt haben. Adresse! = Speicheradresse. Eine Adresse kann wirklich alles gemäß C-Standard sein, obwohl sie letztendlich zu einer Speicheradresse führt. Der Zeiger selbst kann eine ID, ein Offset + Selektor (x86) sein, wirklich alles, solange er (nach der Zuordnung) einen Speicher beschreiben kann Adresse im adressierbaren Raum.
quelle
int i=5
-> i ist 5, dann ist der Zeiger die Adresse ja. Außerdem hat null auch eine Adresse. Normalerweise eine ungültige Schreibadresse (aber nicht unbedingt, siehe x86-Real-Modus), aber dennoch eine Adresse. Es gibt wirklich nur zwei Anforderungen für Null: Es wird garantiert, dass ein Zeiger ungleich mit einem tatsächlichen Objekt verglichen wird, und zwei beliebige Nullzeiger werden gleich verglichen.p
es sich um einen Zeiger handelt,p+1
wird die Adresse nicht immer um 1 erhöht.it's guaranteed to compare unequal to a pointer to an actual object
. Was die Zeigerarithmetik betrifft, sehe ich den Punkt nicht, der Wert des Zeigers ist immer noch eine Adresse, auch wenn die "+" - Operation nicht unbedingt ein Byte hinzufügen wird.Eine andere Art und Weise, in der sich ein C- oder C ++ - Zeiger von einer einfachen Speicheradresse unterscheidet, ist auf die unterschiedlichen Zeigertypen zurückzuführen, die ich in den anderen Antworten nicht gesehen habe (obwohl ich sie aufgrund ihrer Gesamtgröße möglicherweise übersehen habe). Aber es ist wahrscheinlich das wichtigste, weil selbst erfahrene C / C ++ - Programmierer darüber stolpern können:
Der Compiler kann davon ausgehen, dass Zeiger inkompatibler Typen nicht auf dieselbe Adresse verweisen, auch wenn dies eindeutig der Fall ist. Dies kann zu einem Verhalten führen, das mit einem einfachen Zeiger == Adressmodell nicht möglich wäre. Betrachten Sie den folgenden Code (vorausgesetzt
sizeof(int) = 2*sizeof(short)
):Beachten Sie, dass es eine Ausnahme für gibt
char*
, sodass das Bearbeiten von Werten mitchar*
möglich ist (obwohl dies nicht sehr portabel ist).quelle
Kurzzusammenfassung: Die AC-Adresse ist ein Wert, der normalerweise als Speicheradresse auf Maschinenebene mit einem bestimmten Typ dargestellt wird.
Das unqualifizierte Wort "Zeiger" ist mehrdeutig. C hat Zeiger Objekte (Variablen), Zeigertypen , Zeiger Ausdrücke und Zeigerwerte .
Es ist sehr üblich, das Wort "Zeiger" zu verwenden, um "Zeigerobjekt" zu bedeuten, und das kann zu Verwirrung führen. Deshalb versuche ich, "Zeiger" eher als Adjektiv als als Substantiv zu verwenden.
Der C-Standard verwendet zumindest in einigen Fällen das Wort "Zeiger", um "Zeigerwert" zu bedeuten. In der Beschreibung von malloc heißt es beispielsweise, dass "entweder ein Nullzeiger oder ein Zeiger auf den zugewiesenen Speicherplatz zurückgegeben wird".
Was ist eine Adresse in C? Es ist ein Zeigerwert, dh ein Wert eines bestimmten Zeigertyps. (Außer dass ein Nullzeigerwert nicht unbedingt als "Adresse" bezeichnet wird, da er nicht die Adresse von irgendetwas ist).
Die Standardbeschreibung des unären
&
Operators besagt, dass er "die Adresse seines Operanden liefert". Außerhalb des C-Standards wird das Wort "Adresse" üblicherweise verwendet, um sich auf eine (physische oder virtuelle) Speicheradresse zu beziehen, die typischerweise ein Wort groß ist (unabhängig davon, welches "Wort" sich auf einem bestimmten System befindet).Eine "AC-Adresse" wird typischerweise als Maschinenadresse implementiert - genau wie ein C-
int
Wert typischerweise als Maschinenwort implementiert wird. Eine C-Adresse (Zeigerwert) ist jedoch mehr als nur eine Maschinenadresse. Es ist ein Wert in der Regel dargestellt als Maschinenadresse, und es ist ein Wert mit einem spezifischen Typ .quelle
Ein Zeigerwert ist eine Adresse. Eine Zeigervariable ist ein Objekt, das eine Adresse speichern kann. Dies ist wahr, weil der Standard einen Zeiger so definiert. Es ist wichtig, es C-Neulingen mitzuteilen, da C-Neulinge häufig nicht über den Unterschied zwischen einem Zeiger und dem Objekt informiert sind (das heißt, sie kennen den Unterschied zwischen einer Hülle und einem Gebäude nicht). Der Begriff einer Adresse (jedes Objekt hat eine Adresse und das speichert ein Zeiger) ist wichtig, weil er dies aussortiert.
Der Standard spricht jedoch auf einer bestimmten Abstraktionsebene. Die Personen, über die der Autor spricht, die "wissen, worum es bei Adressen geht", die jedoch neu in C sind, müssen unbedingt über Adressen auf einer anderen Abstraktionsebene informiert sein - möglicherweise durch Programmieren der Assemblersprache. Es gibt keine Garantie dafür, dass die C-Implementierung für Adressen dieselbe Darstellung verwendet wie die CPU-Opcodes (in dieser Passage als "Speicheradresse" bezeichnet), über die diese Personen bereits Bescheid wissen.
Er spricht weiter über "vollkommen vernünftige Adressmanipulation". Was den C-Standard betrifft, gibt es grundsätzlich keine "vollkommen vernünftige Adressmanipulation". Addition wird auf Zeigern definiert und das ist es im Grunde. Natürlich können Sie einen Zeiger in eine Ganzzahl konvertieren, einige bitweise oder arithmetische Operationen ausführen und ihn dann zurückkonvertieren. Dies funktioniert standardmäßig nicht garantiert. Bevor Sie diesen Code schreiben, sollten Sie wissen, wie Ihre bestimmte C-Implementierung Zeiger darstellt und diese Konvertierung durchführt. Es wird wahrscheinlich die von Ihnen erwartete Adressdarstellung verwendet, aber es ist nicht Ihre Schuld, weil Sie das Handbuch nicht gelesen haben. Das ist keine Verwirrung, es ist eine falsche Programmierprozedur ;-)
Kurz gesagt, C verwendet ein abstrakteres Konzept einer Adresse als der Autor.
Das Konzept des Autors einer Adresse ist natürlich auch nicht das niedrigste Wort in dieser Angelegenheit. Was bei virtuellen Speicherzuordnungen und physischer RAM-Adressierung über mehrere Chips hinweg die Nummer, auf die Sie der CPU mitteilen, "die Speicheradresse" ist, auf die Sie zugreifen möchten, hat im Grunde nichts damit zu tun, wo sich die gewünschten Daten tatsächlich in der Hardware befinden. Es sind alles Ebenen der Indirektion und Repräsentation, aber der Autor hat eine als Privileg ausgewählt. Wenn Sie dies tun, wenn Sie über C sprechen, wählen Sie die C-Stufe als Privileg !
Persönlich denke ich nicht, dass die Bemerkungen des Autors allzu hilfreich sind, außer im Zusammenhang mit der Einführung von C in Assembly-Programmierer. Für diejenigen, die aus höheren Sprachen kommen, ist es sicherlich nicht hilfreich zu sagen, dass Zeigerwerte keine Adressen sind. Es wäre weitaus besser, die Komplexität anzuerkennen, als zu sagen, dass die CPU das Monopol hat, zu sagen, was eine Adresse ist, und dass daher C-Zeigerwerte "keine" Adressen sind. Sie sind Adressen, aber sie können in einer anderen Sprache geschrieben sein als die Adressen, die er meint. Ich denke, es wäre angemessen, die beiden Dinge im Kontext von C als "Adresse" und "Geschäftsadresse" zu unterscheiden.
quelle
Einfach gesagt, Zeiger sind tatsächlich versetzte Teile des Segmentierungsmechanismus, die nach der Segmentierung in eine lineare Adresse und nach dem Paging in eine physikalische Adresse übersetzt werden. Physische Adressen werden tatsächlich von Ihrem RAM adressiert.
quelle