Was ist der Unterschied zwischen Variablen und Zeigern?

10

Beim Lesen eines Artikels über Unterschiede in der OO- und Funktionsprogrammierung stieß ich auf Funktionszeiger. Es ist eine Weile her, seit ich mein Informatik-Studium (2003) abgeschlossen habe, und so habe ich nach Hinweisen gesucht, um mein Gedächtnis aufzufrischen.

Zeiger sind Variablen, die einen Verweis auf eine Speicheradresse enthalten. Es kann davon ausgegangen werden, dass sie auf die Daten verweisen, die in dieser Speicheradresse enthalten sind, wenn solche Daten vorhanden sind. Oder, wie im Fall des Artikels, geben sie möglicherweise den Einstiegspunkt in einen Codeabschnitt an und können zum Aufrufen dieses Codes verwendet werden.

Warum unterscheidet sich das von einer Variablen? Variablen sind symbolische Namen für Speicheradressen, und Compiler ersetzen den Namen durch die tatsächliche Adresse. Dies bedeutet, dass Variablen Verweise auf Speicherorte enthalten und als Hinweis auf die Daten an dieser Adresse betrachtet werden können, wenn solche Daten vorhanden sind.

Wenn der Unterschied im Verhalten liegt (möglicherweise kann ein Zeiger zur Laufzeit nicht neu zugewiesen werden oder kann nur ein symbolischer Variablenname zugewiesen werden, kein anderer Wert), heißt das nicht, dass es sich nur um eine Variable eines bestimmten Typs handelt, des Zeigertyps? Ebenso wird eine Variable, die als Ganzzahl deklariert ist, durch die Kompilierung dahingehend eingeschränkt, wofür sie verwendet werden kann.

Was fehlt mir hier?

NectarSoft
quelle
4
Nichts, ein Zeiger ist effektiv ein Typ, dessen semantische Interpretation darin besteht, dass der Inhalt der Variablen eine Adresse ist. Beachten Sie natürlich, dass es zwei Adressen gibt, nämlich die Adresse des Zeigers (dh wo die Variable gespeichert ist) und die Adresse, auf die der Zeiger verweist (die tatsächlichen Daten an der Adresse der Variablen). Ein Zeiger ist eine Art Referenz .
Luke Mathieson

Antworten:

8

Ihre Frage ist in mehrfacher Hinsicht interessant, da für verschiedene Themen eine sorgfältige Unterscheidung erforderlich ist. Aber Ihre Vision scheint mir im Wesentlichen richtig zu sein. Ich habe Ihre Referenz nicht gelesen, bevor ich den größten Teil dieser Antwort geschrieben habe, um meine Antwort nicht zu verzerren.

Erstens ist Ihre Aussage Variables are symbolic names for memory addressesfast richtig, verwirrt aber das Konzept und seine übliche Umsetzung. Eine Variable ist eigentlich nur ein Container, der einen Wert enthalten kann, der geändert werden kann. Normalerweise wird dieser Container auf einem Computer als Speicherplatz implementiert, der durch eine Adresse und eine Größe gekennzeichnet ist, da Variablen Objekte enthalten können, die Darstellungen mit mehr oder weniger Informationen erfordern.

Ich werde jedoch hauptsächlich eine abstraktere Sichtweise der Semantik von Sprachen betrachten, unabhängig von Implementierungstechniken.

Variablen sind also aus abstrakter Sicht nur Container. Ein solcher Container muss keinen Namen haben. Sprachen haben jedoch häufig Variablen, die durch Zuordnen eines Bezeichners benannt werden, sodass die Verwendung der Variablen durch den Bezeichner ausgedrückt werden kann. Eine Variable kann durch verschiedene Aliasing-Mechanismen tatsächlich mehrere Bezeichner haben. Eine Variable kann auch ein Teil einer größeren Variablen sein: Ein Beispiel ist eine Zelle einer Array-Variablen, die durch Angabe der Array-Variablen und des Index der Zelle benannt werden kann, aber auch durch Aliasing mit Bezeichnern verknüpft werden kann.

Ich verwende absichtlich den Wortcontainer , der etwas neutral ist, um zu vermeiden, dass andere Wörter aufgerufen werden, die technisch semantisch geladen werden können. Es kommt dem in Wilipedia beschriebenen Referenzkonzept nahe , das häufig mit einer Speicheradresse verwechselt wird. Der Wortzeiger selbst wird oft als Speicheradresse verstanden, aber ich denke nicht, dass dies sinnvoll ist, wenn man die meisten Hochsprachen betrachtet, und wahrscheinlich unangemessen in dem Diskussionspapier, auf das Sie sich beziehen (obwohl Adressen verwendet werden können), da es unangemessen ist Verweis auf eine bestimmte Implementierung. Es ist jedoch für eine Sprache wie C geeignet, die den Implementierungskonzepten und der Maschinenarchitektur viel näher kommen soll.

Wenn Sie Variablen oder Werte auf Implementierungsebene betrachten, kann es tatsächlich mehrere komplexe Indirektionssysteme geben, von "Zeigern auf Maschinenebene", die jedoch für den Benutzer unsichtbar sind (und sein sollten), so dass die abstrakte Sichtweise Ich entwickle kann gültig sein. Bei den meisten Programmiersprachen sollte sich der Benutzer keine Gedanken über die Implementierung machen müssen oder diese überhaupt kennen, da die Implementierung für eine bestimmte Sprache sehr unterschiedlich sein kann. Dies gilt möglicherweise nicht für einige Sprachen wie C, die absichtlich nahe an der Maschinenarchitektur liegen, als fortgeschrittener Ersatz für Assemblersprachen, die in fast direktem Zusammenhang mit der expliziten Binärcodierung stehen, aber für die meisten in den meisten Fällen zu niedrig sind Situationen.

Was der Benutzer einer Sprache wissen sollte und manchmal sogar noch weniger sein sollte, sind Werte und zugehörige Operationen, wo sie enthalten sein können, wie sie Namen zugeordnet werden können, wie das Benennungssystem funktioniert, wie neue Arten von Werten definiert werden, etc.

Ein weiteres wichtiges Konzept sind Bezeichner und Namen. Die Benennung einer Entität (eines Werts) kann durch Zuordnen eines Bezeichners zu einem Wert erfolgen (normalerweise in einer Deklaration). Ein Wert kann aber auch erhalten werden, indem Operationen auf andere benannte Werte angewendet werden. Namen können wiederverwendet werden, und es gibt Regeln (Bereichsregeln), um zu bestimmen, was einem bestimmten Bezeichner je nach Verwendungskontext zugeordnet ist. Es gibt auch spezielle Namen, sogenannte Litterals, um die Werte einiger Domänen zu benennen, z. B. Ganzzahlen (z. B. ) oder Boolesche Werte (z . B. true ).612

Die Zuordnung eines unveränderlichen Werts zu einem Bezeichner wird normalerweise als Konstante bezeichnet. Litterals sind in diesem Sinne Konstanten.

"Wertecontainer" können auch als Werte betrachtet werden, und ihre Zuordnung zu einem Bezeichner ist eine Variable im üblichen "naiven" Sinne, den Sie verwendet haben. Man könnte also sagen, dass eine Variable eine "Containerkonstante" ist.

Nun fragen Sie sich vielleicht, was der Unterschied zwischen der Zuordnung eines Bezeichners zu einem Wert (Konstantendeklaration) oder der Zuweisung eines Werts zu einer Variablen ist, dh dem Speichern des Werts in dem als Containerkonstante definierten Container. Im Wesentlichen kann die Deklaration als eine Operation angesehen werden, die eine Notation definiert, die einen Bezeichner, der eine syntaktische Entität ist, einem Wert zuordnet, der eine semantische Entität ist. Die Zuweisung ist eine rein semantische Operation, die einen Status ändert, dh den Wert eines Containers ändert. In gewissem Sinne ist die Deklaration ein Metakonzept ohne semantischen Effekt, abgesehen von der Bereitstellung eines Namensmechanismus (dh eines syntaktischen Mechanismus) für semantische Entitäten.

Tatsächlich sind Zuweisungen semantische Operationen, die dynamisch auftreten, wenn das Programm ausgeführt wird, während Deklarationen syntaktischer Natur sind und normalerweise unabhängig von der Ausführung im Programmtext interpretiert werden müssen. Aus diesem Grund ist statisches Scoping (dh Text-Scoping) normalerweise der natürliche Weg, um die Bedeutung von Bezeichnern zu verstehen.

Nach all dem kann ich sagen, dass ein Zeigerwert nur ein anderer Name für einen Container ist und eine Zeigervariable eine Containervariable ist, dh ein Container (Konstante), der einen anderen Container enthalten kann (mit möglichen Einschränkungen für das enthaltene Spiel, das von einigen auferlegt wird Typ System).

In Bezug auf Code geben Sie an [pointers] might indicate the entry point to a section of code and can be used to call that code. Eigentlich ist das nicht ganz richtig. Ein Codeabschnitt ist oft allein bedeutungslos (aus Sicht der allgemeinen Ebene oder der Implementierung). Aus einer übergeordneten Sicht enthält Code normalerweise Bezeichner, und Sie müssen diese Bezeichner in dem statischen Kontext interpretieren, in dem sie deklariert wurden. Es gibt jedoch tatsächlich eine mögliche Duplizierung desselben statischen Kontexts, was im Wesentlichen auf eine Rekursion zurückzuführen ist, die ein dynamisches Phänomen (Laufzeitphänomen) darstellt, und der Code kann nur in einer geeigneten dynamischen Instanz des statischen Kontexts ausgeführt werden. Dies ist etwas komplex, aber die Konsequenz ist, dass das richtige Konzept das eines Abschlusses ist, der einen Code und eine Umgebung verknüpft, in der die Bezeichner interpretiert werden sollen. Der Abschluss ist das richtige semantische Konzept, dh ein richtig definierbarer semantischer Wert. Dann können Sie Abschlusskonstanten, Abschlussvariablen, haben

Eine Funktion ist ein Abschluss, normalerweise mit einigen Parametern, um einige ihrer Entitäten (Konstanten und Variablen) zu definieren oder zu initialisieren.

Ich überspringe viele Variationen der Verwendung dieser Mechanismen.

Verschlüsse können verwendet werden, um OO-Strukturen in imperativen oder funktionalen Sprachen zu definieren. Tatsächlich wurden frühe Arbeiten am OO-Stil (wahrscheinlich vor dem Namen) auf diese Weise durchgeführt.

Das Papier, auf das Sie verweisen und das ich schnell überflogen habe, scheint interessant zu sein und wurde von einer kompetenten Person verfasst. Es ist jedoch möglicherweise nicht einfach zu lesen, wenn Sie keine wesentlichen Erfahrungen mit einer Vielzahl von Sprachen und den zugrunde liegenden Rechenmodellen haben.

Aber denken Sie daran: Viele Dinge sind in den Augen des Betrachters, solange er eine einheitliche Sichtweise bewahrt. Die Standpunkte können abweichen.

Beantwortet das deine Frage?

PS: Das ist eine lange Antwort. Wenn Sie einen Teil davon für unzureichend halten, geben Sie bitte ausdrücklich an, um welchen es sich handelt. Danke.

babou
quelle
Ich musste es ein paar Mal durchlesen, aber ich denke, es beantwortet meine Frage, ja. Obwohl meine Definitionen zu implementierungsorientiert sind, scheint die Idee, dass ein Zeiger ein bestimmter Variablentyp ist, zutreffend zu sein, dh "ein Zeigerwert ist nur ein anderer Name für einen Container, und eine Zeigervariable ist eine Containervariable", wo ich dies nicht tue Sie unterscheiden zwischen einem Zeiger, der die Adresse eines Codeblocks enthält, und einem Container, der einen Container enthält, der einen Abschluss enthält. Ist der Unterschied zwischen dem eines statischen Kontexts und dem eines Programms in Ausführung?
NectarSoft
1
@NectarSoft Die Unterscheidung erfolgt nur zwischen Codeblock und Schließung. Was ich damit sagen will, ist, dass ein Codeblock für sich genommen, isoliert von jedem Kontext, normalerweise nichts bedeutet. Wenn ich Ihnen sage, dass " wenn die Mome Ratten größer sind als Borogoves, dann sind die Toves outgrabe ", bedeutet der Satz nichts, weil Sie den Kontext verpassen, in dem all diese Konzepte definiert sind. Dieser statische Kontext ist normalerweise der Text, der das Codefragment einschließt. Das Problem ist, dass dieser statische Kontext selbst mehrere Laufzeitinstanzen haben kann und das Codefragment nur auf eine von ihnen verweisen darf.
Babou
1
@NectarSoft (Fortsetzung) Der Abschlusswert ist also die Zuordnung des Codefragments und einer dynamischen Instanz des statischen Kontexts, die diesem Fragment eine Bedeutung verleiht. Eine Zuordnung desselben Codefragments zu einer anderen dynamischen Instanz desselben statischen Kontexts führt zu einem anderen Abschluss. Normalerweise verwendet Ihr Code eine Variable, die zum Kontext gehört, aber es ist eine andere Variable für jede dynamische Instanz des Kontexts, obwohl der Name (statisch definiert) gleich bleibt. Klärt dies das Problem oder sollte ich ein Beispiel in der Antwort erstellen (das Kommentarformat ist eingeschränkt)?
Babou
Es klärt das Problem, danke, während es andere Fragen aufwirft, über die ich einige Zeit nachdenken werde. Wenn zum Beispiel für jeden Abschluss ein eigener Zeiger erforderlich ist, scheint der Zeiger Teil des dynamischen Kontexts zu werden und gehört daher zum Abschluss! Ich frage mich auch über Schließungsgrenzen und Schließungshierarchien, da jeder dieser Kontexte, die einem statischen Codeblock zugeordnet sind und auf die ein Zeiger verweist, notwendigerweise eine Variable in einem eigenen Abschluss sein wird. All dies ist jedoch
kein
@NectarSoft Eigentlich. Wenn Sie einen Abschluss erstellen, versuchen Sie, den mit dem Code verknüpften Kontext auf das zu beschränken, was erforderlich ist, um diesem Code die richtige Bedeutung zu geben (bis zu einigen praktischen Einschränkungen, um die Verwaltung von Informationen durch Mikrometer zu vermeiden). Dies kann statisch entschieden werden.
Babou
2

Der Unterschied besteht per Definition und Anwendung darin, dass ein Zeiger eine spezialisierte Variable ist, die eine Speicheradresse einer anderen Variablen enthält. In OO-Begriffen würde ein Zeiger möglicherweise sein Verhalten von einer allgemeinen Klasse namens Variable erben.

Ricardo
quelle