Pointer
Grundsätzliches:
Im Arbeitsspeicher werden Daten gespeichert. Um auf die Daten eindeutig zugreifen zu können, werden diesen Daten Adressen zugeordnet. Diese sind fest eingebrannt und können nicht durch ein Programm verändert werden. Verändert werden können nur die Daten.
Vergleich: Jedes Haus hat eine feste Adresse, die von einem Amt fest vorgegeben werden und vom Hauseigentümer nicht verändert werden dürfen. Nur die in jedem Haus wohnenden Menschen können geändert werden (z.B. Wohnungswechsel).
Man kann sich einen Arbeitsspeicher schematisch wie folgt vorstellen:
Ausschnitt aus einem Arbeitsspeicher AdresseWert Fest eingebrannte, unveränderliche Werte veränderliche Werte (die durch ein Programm geändert werden können). Wenn nichts anderes angegeben wird, besteht der Wert immer aus 1 Byte. Also ist 1 Byte die kleinste adressierbare Speichereinheit.
Durch die Deklaration einer Variable wird im Arbeitsspeicher an einer bestimmten Adresse Platz reserviert. Wird zusätzlich bei der Deklaration die Variable noch z.B. mit 5 initialisiert, bekommt der Wert an dieser Adresse 5 zugewiesen. Wird die Variable nicht initialisiert, ist der Wert an dieser Adresse unbestimmt (undefiniert).
Bitte folgendes Programm abschreiben
... int x = 5; int *ptr; ptr = &x; *ptr = 13; 01235x... Programm- Ausschnitt Arbeitsspeicher - Ausschnitt Was veranlasst diese Anweisung im Arbeitsspeicher ? Es wird im Arbeitsspeicher an einer bestimmten Adresse Platz für eine integer-Zahl reserviert und die Zahl 5 der Variablen x zugewiesen. Auf die (Anfangs)Adresse dieses Speicherbereichs hat der Programmierer keinen Einfluß. Diese legt der Compiler bzw. Programmlader fest. AdresseWert
... int x = 5; int *ptr; ptr = &x; *ptr = 13; 01235x ptr... Programm- Ausschnitt AdresseWert Inhalt von Adresse von Deklaration eines Pointers: d.h. Wert von ptr ist eine Adresse Compiler speichert hier in Wirklichkeit die Variablen direkt hintereinander ab (Speicher wird nicht fragmentiert). Arbeitsspeicher - Ausschnitt Wert undefiniert: wir nehmen diesen Wert einfach mal an.
... int x = 5; int *ptr; ptr = &x; *ptr = 13; 01235x ptr... Programm- Ausschnitt AdresseWert Arbeitsspeicher - Ausschnitt
... int x = 5; int *ptr; ptr = &x; *ptr = 13; 01235x ptr... Programm- Ausschnitt AdresseWert Arbeitsspeicher - Ausschnitt
... int x = 5; int *ptr; ptr = &x; *ptr = 13; 01235x ptr... Programm- Ausschnitt 0123 AdresseWert Arbeitsspeicher - Ausschnitt
... int x = 5; int *ptr; ptr = &x; *ptr = 13; x ptr... Programm- Ausschnitt 0123 AdresseWert Arbeitsspeicher - Ausschnitt
Ein Pointer ist eine Variable, deren Wert die Adresse eines Speicherplatzes (z.B.einer Variable) ist. Merke:
Unterschied Pointer und " normaler " Wert im Arbeitsspeicher. Dazu ein Beispiel aus dem " Alltag " : Bei Absitzen einer Gefängnisstrafe muß sich eine Person ( " normaler " Wert) in der Zelle (Teil des Arbeitsspeicher) befinden. Es ist nicht zulässig, statt einer Person ( " normaler " Wert) eine Telefonnumer (Adresse) zu hinterlegen.
Ein Pointer ist also ein Verweis. Man sagt dazu auch Zeiger, Referenz, Link
Beispiel:
Bitte folgende Aufgabe 1 in Gruppen zu je zwei Leuten lösen (siehe Aufgabenblatt oder folgende Folie)
... int a = 7; int b = 3; int c = 5; int *ptr; ptr = &a; a = a + b + c; a b c ptr... Aufgabe: Welchen Wert hat *ptr nach Programmende ? Wert undefiniert: wir nehmen diesen Wert einfach mal an.
... int a = 7; int b = 3; int c = 5; int *ptr; ptr = &a; a = a + b + c; a b c ptr...
int a = 7; int b = 3; int c = 5; int *ptr; ptr = &a; a = a + b + c; a b c ptr...
int a = 7; int b = 3; int c = 5; int *ptr; ptr = &a; a = a + b + c; a b c ptr...
int a = 7; int b = 3; int c = 5; int *ptr; ptr = &a; a = a + b + c; a b c ptr... Welchen Wert hat *ptr
Beispiel:
Bitte folgende Aufgabe 2 in Gruppen zu je zwei Leuten lösen (siehe Aufgabenblatt oder folgende Folie)
... float e = 3.1; float f = 2.7; float *p; p = &e; *p = f +*p; e f p... Aufgabe: Welchen Wert hat e nach Programmende ? Wert undefiniert: wir nehmen diesen Wert einfach mal an.
... float e = 3.1; float f = 2.7; float *p; p = &e; *p = f +*p; e f p...
float e = 3.1; float f = 2.7; float *p; p = &e; *p = f +*p; e f p...
float e = 3.1; float f = 2.7; float *p; p = &e; *p = f +*p; e f p
... float e = 3.1; float f = 2.7; float *p; p = &e; *p = f +*p; e f p... Welchen Wert hat e
Pointer und Felder
... int v[3] = {20,5,10}; v[0] ?5v[1] ?10v[2]... Aufgabe: Wie heißen die Adressen von v[1] und v[2] Tipp: Debugger von Visual C++ benutzen
Bitte folgende Aufgabe 3 in Gruppen zu je zwei Leuten lösen (siehe Aufgabenblatt oder folgende Folie)
... int v[3] = {20,5,10}; v[0] 01425v[1] v[2]...
daß die Zahl 20 vier Byte, die Zahl 5 vier Byte und die Zahl 10 auch 4 Byte belegt... Die Darstellung rechts ist eine Abkürzung für die Darstellung links (folgt gleich) und bedeutet int braucht 4 Byte. Wie die Zahlen (20 bzw. 5 bzw. 10) jeweils auf die 4 Byte verteilt wird, ist für uns uninteressant. Falls es jemand interessiert (nächste Folie)
Dualdarstellung (als 4 Byte) von 20 ist: Das 1. Byte kommt nach... Das 2. Byte kommt nach... Das 3. Byte kommt nach... Das 4. Byte kommt nach... Aufgabe: Bestimmen Sie die restlichen Bytes dieser Tabelle
Bitte Aufgabe 4 in Gruppen zu je zwei Leuten lösen (siehe Aufgabenblatt oder folgende Folie)
... double d[3] = {3.1,2.7,6.2}; d[0] ?2.7d[1] ?6.2d[2]... Aufgabe: Wie heißen die Adressen von v[1] und v[2] Tipp: Debugger von Visual C++ benutzen
... double d[3] = {3.1,2.7,6.2}; d[0] d[1] d[2]...
Ergebnis Die Adresse hängt von der Grösse des Datentyps ab.
Beispiel: Wie entwickeln sich die Werte (in der Tabelle) der folgenden Variablen dynamisch während des Programmablaufs ?
int v[3] = {10,20,30}; int *p,*q,*r; v[0] v[1] v[2] p q p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); Werte undefiniert: wir nehmen diesen Wert einfach mal an.
013810v[0] v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013810v[0] v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013810v[0] v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013810v[0] v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013810v[0] v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013810v[0] v[1] v[2] p q q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; = 0139 *(r-2)= 50; Wo würde die 3 abgespeichert werden, wenn der Compiler so rechnen würde ? Schauen wir uns den Arbeitsspeicher genauer an...
0138v[0] 0139 v[1] q = r-2; 0143 *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; = 0139 *(r-2)= 50; Wo würde die 3 abgespeichert werden? Schauen wir uns den Arbeitsspeicher genauer an... int braucht 4 Byte. Wie die Zahl (10 bzw. 20) auf die 4 Byte verteilt wird, ist für uns uninteressant
0138v[0] 0139 v[1] q = r-2; 0143 *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; = 0139 *(r-2)= 50; ab der Adresse 0139 werden 4 Byte abgespeichert.
0138v[0] 0139 v[1] q = r-2; 0143 *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; = 0139 *(r-2)= 50;
0138v[0] 0139 v[1] q = r-2; 0143 *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; = 0139 *(r-2)= 50; Es wird nicht ein Speicherblock, sondern (Teile) zweier Speicherblöcke (v[0] und v[1]) überschrieben: zwei Zahlen "beschädigt" (versaut) Deswegen rechnet der Compiler bei Adressrechnungen wie folgt..
013810v[0] v[1] v[2] p q q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; *4 = 0142 *(r-2)= 50; !
013810v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; *4 = 0142
Bitte diese angefangene Aufgabe (= Aufgabe 5 im Aufgabenblatt) vollends selbständig lösen.
013810v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; *4 = 0138
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; *(r-2)= 50; // *4=0138
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; *4 0146
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3; 4= 4=
013850v[0] 01423v[1] v[2] p q *(r-2)= 50; q = r-2; r *r = *p + *(q+1); p = r; *p = *p + *(q+1) + *(r-2); int v[3] = {10,20,30}; int *p,*q,*r; p = &v[0]; q = &v[1]; r = &v[2]; *(p+1)= 3;
Noch ein paar Infos:
Der Wert eines Pointers wird mit %p ausgegeben. Siehe folgendes Beispiel:
int main (){ int zahl=5; int *ptr; ptr = &zahl; printf("Adresse=%p Inhalt=%d", ptr, *ptr); return 0; }
Warum macht das folgende Programm während der Laufzeit Probleme?
int main (){ int *ptr; *ptr = 4711; return 0; } Die Variable ist nicht initialisiert. Deshalb steht in ptr eine uns unbekannte Adresse. An dieser Adresse wurde aber kein Speicher reserviert. Deshalb könnte an dieser Adresse ein Teil des Betriebssystems stehen. Der Inhalt dieser Adresse ist also nicht reservierter Speicher. Auf diesen wird zugegriffen. Moderne Prozessoren merken dies und deshalb wird auf dem Bildschirm eine entsprechende Meldung ausgegeben. Konkretes Beispiel: Siehe nächste Folie!
int main (){ int *ptr; *ptr = 4711; return 0; } 0815 ptr Teil des Syst. Betr. Wert undefiniert: wir nehmen diesen Wert einfach mal an. Was steht an der Adresse 0912 im Arbeitsspeicher ? Der Programmierer weiß es nicht, denn er hat an dieser Adresse keinen Speicherplatz reservieren lassen und diesen entsprechend belegt. In einem schlimmen Fall könnte an der Adresse 0912 zum Beispiel... ein Teil des Betriebssystems stehen !
int main (){ int *ptr; *ptr = 4711; return 0; } 0815 ptr Wert undefiniert: wir nehmen diesen Wert einfach mal an. Was steht an der Adresse 0912 im Arbeitsspeicher ? Der Programmierer weiß es nicht, denn er hat an dieser Adresse keinen Speicherplatz reservieren lassen und diesen entsprechend belegt. In einem schlimmen Fall könnte an der Adresse 0815 zum Beispiel... ein Teil des Betriebssystems stehen !