Willkommen auf meiner Seite – schön, dass du da bist!
Diese Seite ist noch in Bearbeitung... die Inhalte werden ggf. noch ergänzt und möglicherweise korrigiert.
Bitte berücksichtigen!
In der Sprache C existiert ein Konstrukt das als Zeiger oder Pointer bezeichnet wird. Zeiger repräsentieren eine besondere Art von Variablen. Ihr Inhalt ist eigentlich nebensächlich. Viel interessanter ist, dass sie auf andere Variablen zeigen können. Man betrachtet also nicht die Zeigervariable selbst, sondern über die Zeigervariable auf den Inhalt einer anderen Variablen. Diese Indirektion ist nicht ganz leicht zu verstehen und bietet auch bei erfahrenen Programmieren immer wieder Anlass zur Verwirrung. Aus diesem Grund soll im folgenden Beitrag die Funktionsweise eines Zeigers erklärt werden.
Eine Variable in einer Programmiersprache ist ein benannter Speicherplatz, der einen Wert enthalten. Variablen werden zum speichern von Informationen verwendet. Eine Variable muss üblicherweise deklariert werden. D.h. man ordnet ihr einen eindeutigen Namen und einen Datentyp zu. Diese Informationen werden vom Compiler dann in einer sogenannten Symboltabelle abgelegt und verwaltet. Nach dem folgende Deklaration durchgeführt wurde:
int myVar;
Wird in der Symboltabelle folgender Eintrag vorgenommen:
Bei diesem Vorgang fordert der Compiler vom Speichermanagement des Systems eine Adresse an, wo die damit zusammenhängenden Informationen dann gespeichert werden. Diese Speicheradresse wird als L-Wert bezeichnet, und gibt an, wo sich die Variable im Speicher befindet also (L-Wert = Standort). Wenn wir einer Variablen nun einen konkreten Wert zuweisen, navigieren wir direkt zum Speicherort der Variablen im Speicher (dem L-Wert) und aktualisieren den Speicher an dieser Adresse mit dem neuen Wert. Die Daten, die tatsächlich im Speicher gespeichert sind, werden als R-Wert (R-Wert = Registerwert) bezeichnet. Die Anzahl der dazu benötigten Speicherplätze hängt massgeblich vom Typ der Variablen ab.
Nachdem wir uns nun mit den Variablen und ihrer Funktionsweise vertraut sind, können wir uns dem Zeiger-Konstrukt zu wenden. Einfach ausgedrückt ist ein Zeiger nichts anderes als eine Variable, die auf die Speicheradresse einer anderen Variablen verweist.
Ein Zeiger ist eine Variable, die die Adresse einer anderen Variable speichert – also nicht den Wert selbst, sondern wo dieser Wert im Speicher liegt. Die Definition eines Zeigers besteht aus dem Datentyp des Zeigers und dem gewünschten Zeigernamen. Der Datentyp eines Zeigers besteht wiederum aus dem Datentyp des Wertes, auf den gezeigt wird, sowie aus einem Asterisk. Ein Datentyp eines Zeigers wäre also z. B. double*.
Die Adresse einer Variablen kann vom Programmierer zwar nicht festgelegt, aber über den unären Adressoperator & ermittelt werden.Die Adresse eines Datenobjektes ist über die gesamte Laufzeit des Programms unveränderlich. Der Adressoperator kann auf alle Datenobjekte mit Ausnahme von Bitfeldern und Datenobjekten der Speicherklasse register angewendet werden. Hat man eine Adresse zur Verfügung, kann man mithilfe des Inhaltsoperators * auf den Wert, der an dieser Adresse gespeichert ist, zugreifen. Der Stern in der Definition eines Zeigers * ist nicht der Inhaltsoperator *, es wird also dasselbe Symbol für unterschiedliche Zwecke verwendet. Bei der Verwendung des Inhaltsoperators auf einen Zeiger spricht man auch von der Dereferenzierung des Zeigers.
Unter Verwendung der soeben erlernten Terminologie ist ein Zeiger somit eine Variable, deren R-Wert der L-Wert einer anderen Variablen ist. Die Deklaration einer Zeiger-Variable ist recht einfach. Um eine Zeigervariable zu definieren, wird zunächst der Typ angegeben, auf den der Zeiger zugreifen kann. Es folgt ein Stern und dann der Name der Variablen. Die Adresse einer Variablen kann vom Programmierer zwar nicht festgelegt, aber über den unären Adressoperator & ermittelt werden. Die Adresse eines Datenobjektes ist über die gesamte Laufzeit des Programms unveränderlich.
int myPointer;
Hat man eine Adresse zur Verfügung, kann man mithilfe des Inhaltsoperators * auf den Wert, der an dieser Adresse gespeichert ist, zugreifen. Der Typbezeichner (in unserem Fall int) muss mit dem Datentyp der Variablen übereinstimmen, mit der der Zeiger verwendet werden soll. Das Sternchen zeigt dem Compiler an, dass myPointer ein Zeiger ist. Da Leerzeichen in C keine Rolle spielen, kann das Sternchen an einer beliebigen Stelle zwischen dem Typbezeichner und dem Namen der Zeigervariablen platziert werden, so dass manchmal auch Folgendes angezeigt wird: int * myPointer, int* myPointer usw.
Ein Zeiger, der definiert ist, aber nicht auf irgendetwas zeigt, ist für sich genommen ein ziemlich sinnloser Zeiger. Um auf die Speicheradresse einer anderen Variablen zu verweisen, müssen wir dem Zeiger natürlich die Speicheradresse dieser Variablen zuweisen. Dazu wird der sogenannte Adress-of-Operator & verwendet. Um unseren neuen Zeiger auf den Speicherort unserer Wert-Typ-Variablen myVar zu richten, rufen wir einfach die folgende Anweisung auf:
myPointer = &myVar;
Dies vervollständigt den im vorherigen Diagramm gezeigten Link und wird als Referenzierung bezeichnet. Aus dem gleichen Grund wird der Adressoperator & auch als Referenzierungsoperator bezeichnet.
void setup() {
Serial.begin(9600);
int myVar = 10; // Initialize a variable.
Serial.print("myVar: ");
Serial.println(myVar);
Serial.print("myVar's L-Value: ");
Serial.println((long) &myVar, DEC); // Grab myVar's lvalue
Serial.print("myVar's R-Value: ");
Serial.println(myVar, DEC);
Serial.println("Nun die Zeiger Werte: *myPointer ...");
int *myPointer; // Declare your pointer.
myPointer = &myVar; //Assign myVar's memory address to pointer.
Serial.print("myPointer's L-Value: ");
Serial.println((long) &myPointer, DEC); //myPointer's lvalue
Serial.print("myPointer's R-Value: ");
Serial.println((long) myPointer, DEC); //myPointer's rvalue
}
void loop() {}
---------Monitor---------------------
myVar: 10
myVar's L-Value: 2298
myVar's R-Value: 10
Nun die Zeiger Werte: *myPointer ...
myPointer's L-Value: 2296
myPointer's R-Value: 2298
Wir haben gerade gesehen, dass ein Zeiger auf eine Stelle im Speicher verweisen kann, indem er diesem Zeiger die Speicheradresse einer Variablen mit dem Referenzoperator (&) zuweist. Wir können noch einen Schritt weiter gehen und den an dieser Speicheradresse gespeicherten tatsächlichen Wert erhalten, indem wir den Zeiger dereferenzieren. Dies wird auch als Indirektion bezeichnet und erfolgt über den Indirektionsoperator (*) mit Ihrem Zeiger.
void setup() {
Serial.begin(9600);
int myVar = 10;
Serial.println();
Serial.println("**-------------Start Zeiger Demo------------------**");
Serial.println();
Serial.print("myVar's Lvalue: "); Serial.println((long) &myVar, DEC);
Serial.print("myVar's Rvalue: "); Serial.println(myVar, DEC);
Serial.println();
int *myPointer;
myPointer = &myVar;
Serial.print("myPointer's Lvalue: "); Serial.println((long) &myPointer, DEC);
Serial.print("myPointer's Rvalue: "); Serial.println((long) myPointer, DEC);
Serial.println();
*myPointer = 5; // THIS IS OUR DEREFENCING ADDITION.
Serial.println("-----------------------");
Serial.println("Updating *myPointer = 5");
Serial.println();
Serial.print("myPointer's Lvalue: "); Serial.println((long) &myPointer, DEC);
Serial.print("myPointer's Rvalue: "); Serial.println((long) myPointer, DEC);
Serial.println();
Serial.print("myVar's Lvalue: "); Serial.println((long) &myVar, DEC);
Serial.print("myVar's Rvalue: "); Serial.println(myVar, DEC);
Serial.println();
Serial.println("**------------End-------------------**");Serial.println();
}
void loop() {}
---------Monitor---------------------
**-------------Start------------------**
myVar's Lvalue: 2298
myVar's Rvalue: 10
myPointer's Lvalue: 2296
myPointer's Rvalue: 2298
-----------------------
Updating *myPointer = 5
myPointer's Lvalue: 2296
myPointer's Rvalue: 2298
myVar's Lvalue: 2298
myVar's Rvalue: 5
**------------End-------------------**