Kapitel 5

Werbung
EINI-I
Einführung in die Informatik
für Naturwissenschaftler und
Ingenieure I
Kapitel 5
Claudio Moraga; Gisbert Dittrich
FBI Unido
[email protected]
Vorl “EINI-I"
Kap 5: Zeiger
Gliederung Kapitel 5
• Zeiger
–
–
–
–
–
20.11.2000
Grundbegriffe
Zeiger und Felder
Beispiel: Zeichenketten
Funktionen als Parameter
Mehrdimensionale Felder
2
Vorl “EINI-I"
Kap 5: Zeiger
Variable
• hat:
– Namen
– Typ
• u.a. Wertebereich, aus dem Werte angenommen
werden
– Wert (aus dem Wertebereich oder "undefiniert")
• ist realisiert im Speicher durch:
– Speicherplatz, der
• hat : Adresse
• Anmerkung: Nicht immer alle Angaben verfügbar !
20.11.2000
3
Vorl “EINI-I"
Kap 5: Zeiger
Variable
• Anmerkungen:
– Adresse: Eindeutiger Identifikator des Speicherplatzes
– Größe des Speicherplatzes abhängig vom Typ der
Variablen.
Speicherplatz
– Bildliche Darstellungen:
Name
Adresse
Wert
oder auch:
Beispiel:
Adresse
123
Wert
20.11.2000
Typ nicht explizit
angegeben !
Name
-99
B
4
Vorl “EINI-I"
Kap 5: Zeiger
Grundbegriffe Zeiger
• Neu: Adressen können jetzt Werte von
Variablen sein !! (Typisiert!)
• Zeiger:
– Vereinbarung: T *var;
– var ist Variablenname, der Adressen von Variablen
vom Typ "T" annimmt.
•Typ int
Beispiel:
var
20.11.2000
34567
1230
1230
-99
B
5
Vorl “EINI-I"
Kap 5: Zeiger
Grundbegriffe Zeiger
Beispiel:
var
34567
1230
1230
-99
B
Gängige alternative Darstellung:
Beispiel:
var
var
20.11.2000
34567
1230
1230
-99
-99 B
6
Vorl “EINI-I"
Kap 5: Zeiger
Grundbegriffe Zeiger
• Sei var Variablenname:
&var liefert die Adresse von var
• Sei wo Name eines Zeigers (Speicherreferenz),
so liefert
*wo die Variable, auf die wo zeigt.
• Referenzen sind typisiert: es wird angegeben,
welcher Typ sich hinter einer Adresse verbirgt
(z. B. Referenz auf einen Wert vom Typ int).
20.11.2000
7
Vorl “EINI-I"
Kap 5: Zeiger
Beispiele
int k;
float *t;
&k ist die Adresse der ganzen Zahl k,
t ist als Zeiger auf eine Variable vom Typ
float definiert, beinhaltet also eine Adresse
adr einer derartigen Variablen
*t = 17.14 speichert damit den Wert 17.14
unter dieser Adresse adr
20.11.2000
8
Vorl “EINI-I"
Kap 5: Zeiger
Beispiele
Definiert seien
int A=1, B=-99;
int *ZeigerA, *ZeigerB;
D.h.: die Variablen ZeigerA und ZeigerB
enthalten Adressen ganzer Zahlen.
Nach ZeigerA = &A hat ZeigerA also als Wert
die Adresse der Variablen A:
ZeigerA
1
A
-99
B
ZeigerB
20.11.2000
9
Vorl “EINI-I"
Kap 5: Zeiger
Beispiele (Forts.)
Es gilt *ZeigerA == 1
Nach *ZeigerA = B enthält der Speicherplatz,
dessen Adresse ZeigerA ist, den Wert von B,
also:
ZeigerA
9801
-99
1
A
9801
-99
B
Situation nach:
B = B * B;
ZeigerB = &B;
*ZeigerA = *ZeigerB;
20.11.2000
ZeigerB
10
Vorl “EINI-I"
Kap 5: Zeiger
Merke
• *ZeigerA spricht den Speicherplatz an, auf
den ZeigerA zeigt:
weil ZeigerA eine Referenz ist, gibt *ZeigerA den
Inhalt dieser Referenz an (man spricht von
Dereferenzieren: von der Referenz/Adresse zum
dadurch bez. Speicherplatz übergehen)
• &B auf der linken Seite einer Zuweisung ist
illegal:
die Adressen werden von Compiler oder vom
Laufzeitsystem gesetzt, aber nicht vom Benutzer
20.11.2000
11
Vorl “EINI-I"
Kap 5: Zeiger
Beispiel
Vertausche zwei Werte
Was geschieht in Tausch?
void Tausch (int a, int b) {
Der Tausch
int temp;
bleibt lokal auf
temp = a;
die Funktion
a = b;
b = temp;
beschränkt (wg.
}
call by value)
20.11.2000
12
Vorl “EINI-I"
Kap 5: Zeiger
Beispiel
Was geschieht in AdrTausch?
void AdrTausch(int *p, int *q) {
int temp;
temp nimmt den
temp = *p;
Inhalt von *p auf
*p = *q;
*q = temp;
}
Der Inhalt von
*q
wird als Inhalt
Der Inhalt von *q ist der
von *p
in temp gespeicherte Wert
gespeichert
20.11.2000
13
Vorl “EINI-I"
Kap 5: Zeiger
Beispiel (Bildchen)
p
q
-121
30
-121
30
temp = *p;
*p = *q;
*q = temp;
temp
undef
30
Dadurch ist sog. call by reference möglich
20.11.2000
14
Vorl “EINI-I"
Kap 5: Zeiger
// K5-P1:
// Tauschen Adressen als Parameter
//
// Demonstriert Adressen als Parameter
//
#include <iostream.h>
void Tausch (int, int);
void AdrTausch(int *, int *); // Funktionsprototypen
main() {
void Tausch(int, int);
void AdrTausch(int *, int *);
int x = 30, y = -121;
cout << "vor Tausch: x = " << x << ",
Tausch(x, y);
cout << "nach Tausch: x = " << x << ",
cout << "oh! (klar: call by value)\n";
cout << "vor AdrTausch: x = " << x <<
AdrTausch(&x, &y);
cout << "nach AdrTausch: x = " << x <<
}
20.11.2000
y = " << y << endl;
y = " << y << endl;
", y = " << y << endl;
", y = " << y << endl;
15
Vorl “EINI-I"
Kap 5: Zeiger
void Tausch (int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
void AdrTausch (int *p, int *q) {
int temp;
temp = *p;
*p = *q;
*q = temp;
}
• Ausführen
20.11.2000
16
Vorl “EINI-I"
Kap 5: Zeiger
Felder und Zeiger
• Sei deklariert
int a[10], x, *pa;
• Dann:
pa = &a[0] setzt pa als Zeiger auf das
erste Element von a
x = *pa
20.11.2000
würde also a[0] nach x kopieren.
17
Vorl “EINI-I"
Kap 5: Zeiger
Felder und Zeiger
• pa+1,..., pa+9 zeigen auf die Elemente
a[1],...,a[9]
– es gilt also *(pa+i) = a[i]
für i = 0, ..., 9
• Allgemein: ist deklariert T *p;(p ist also ein
Zeiger auf Elemente vom Typ T), dann
bezeichnet p+i das Element vom Typ T, das
von p um i*sizeof(T) entfernt liegt.
20.11.2000
18
Vorl “EINI-I"
Kap 5: Zeiger
Felder und Zeiger
• pa ist eine Zeiger-Variable, a ist ein Feld mit
Elementen vom Typ int, also sind z. B. a=pa,
a++ illegal.
• Bei Funktionsaufrufen werden Felder als
aktuelle Parameter als Zeiger auf das jeweils
erste Element interpretiert!
• Damit erklärt sich, daß Kopiere korrekt
funktioniert.
20.11.2000
19
Vorl “EINI-I"
Kap 5: Zeiger
Felder und Zeiger
void strcpy (char nach[ ], char von[ ]) {
int i = 0;
while ((nach[i] = von[i]) ! = '\0') i++;
}
Kopiert bekanntlich die Zeichenkette von in
die Zeichenkette nach.
Die Zeichenketten werden als Felder dargestellt.
20.11.2000
20
Vorl “EINI-I"
Kap 5: Zeiger
Felder und Zeiger
void strcpy (char *s, *t) {
while ((*s++ = *t++) ! = '\0');
}
*s++ dereferenziert s und schaltet die Adresse dann
um sizeof(char) weiter
[also zu lesen (*s)++].
20.11.2000
21
Vorl “EINI-I"
Kap 5: Zeiger
Felder und Zeiger
• merke:
(*s++ = *t++) ! = '\0'
liefert den Wert 0, falls das Ende der
Zeichenkette t erreicht ist (dann soll ja auch die
while-Schleife abbrechen)
• die eigentliche Arbeit findet in dieser Zuweisung
statt, daher ist der Anweisungsblock in der
while-Schleife leer.
20.11.2000
22
Vorl “EINI-I"
Kap 5: Zeiger
Vergleich von Zeichenketten
Sind a und b Zeichenketten, so soll der
ganzzahlige Wert strcmp(a, b) den
lexikographischen Vergleich von a und b
ermöglichen. Es soll gelten:
– strcmp(a, b) ist negativ, wenn a kleiner als b ist,
– strcmp(a, b) ist Null, wenn a gleich b ist,
– strcmp(a, b) ist positiv, wenn a größer als b ist,
20.11.2000
23
Vorl “EINI-I"
Kap 5: Zeiger
Vergleich von Zeichenketten
int strcmp (char *s, char *t) {
int k = 1;
for (;*s == *t; s++, t++)
if (*s == '\0') k = 0;
if (k > 0) k = (*s - *t);
return k;
Die Zeichenketten
}
werden durchlaufen, solange
sie identische Zeichen haben
sonst wird die
Differenz berechnet
20.11.2000
tritt dabei das Ende einer
Kette auf, sind sie identisch
24
Vorl “EINI-I"
Kap 5: Zeiger
Vergleich von Zeichenketten
Version mit Feldern
int strcmp (char s[], char t[]) {
int i = 0, k = 1;
while (s[i] == t[i])
if (s[i++] == '\0')
k = 0;
if (k > 0) k =(s[i] - t[i]);
return k;
}
Felder und Zeiger
20.11.2000
25
Vorl “EINI-I"
Kap 5: Zeiger
// K5-P2: Zeiger und Felder
//
// Demonstriert Zeiger und Felder
//
#include <iostream.h>
main(){
int v1[10];
int i;
// Feld v1 initialisieren
cout << "ursprüngliches Feld v1:\n";
for (i=0;i<10;i++)
{ v1[i] = 100 + i;
cout << v1[i] << " ";
}
20.11.2000
26
Vorl “EINI-I"
Kap 5: Zeiger
cout << "\n\n" << "kopiertes Feld z1:\n";
// kopieren und Ausgabe ueber Zeiger
int *z1;
z1 = v1;
for (i=0;i<10;i++){
cout << *z1 << "
z1=z1+1;
}
cout << '\n';
";
}
• Ausführen
20.11.2000
27
Vorl “EINI-I"
Kap 5: Zeiger
Zur Erinnerung
int v1[10];
Adr.
v1
v1
v1[0]
...
v1[9]
...
int *z1;
z1 = v1;
z1 = z1+1;
z1
20.11.2000
28
Vorl “EINI-I"
Kap 5: Zeiger
#include <iostream.h>
main(){
int v1[10]; int i;
cout << "ursprüngliches Feld v1:\n";
for (i=0;i<10;i++)
{ v1[i] = 100 + i;
cout << v1[i] << " ";
}
cout << "\n\n" << "kopiertes Feld z1:\n";
int *z1;
z1 = v1; // Feldname als Zeiger
for (i=0;i<10;i++){
cout << *z1 << " ";
z1=z1+1;
}
cout << '\n';
}
20.11.2000
29
Vorl “EINI-I"
Kap 5: Zeiger
Funktionen als Parameter
• In C++ nicht direkt möglich.
• Wohl aber: Übergabe eines Zeigers auf eine
Funktion
Zeiger auf Funktionen
Beachte: int (*f)() im Vergleich zu
int *f():
•int (*f)(): Zeiger auf eine Funktion ohne
Argumente mit Rückgabe Wert int
•int *f(): Funktion ohne Argument,
die einen Zeiger auf int zurückgibt
20.11.2000
30
Vorl “EINI-I"
Kap 5: Zeiger
Zeiger auf Funktionen; Namen von Funktionen
Adr.
BspFunktion
Funktionsname
Speicherbereich, wo
das Programm der
Funktion
abgespeichert ist
z
char BspFunktion(char, char);
char (*z)(char, char);
z = BspFunktion;
// Zeiger auf Funktion gleicher Art
// Übergabe der Adresse der Funktion
cout << (*z)(‘A‘, ‘B‘) << endl;
20.11.2000
// Funktionsprototyp
// Funktionsaufruf über z
31
Vorl “EINI-I"
Kap 5: Zeiger
#include<iostream.h>
int max(int x, int y){
int k ;
if (x > y) k = x;
else k = y;
return k;
}
int min(int x, int y){
int k ;
if (x < y) k = x;
else k = y;
return k;
}
main ( ) {
int a = 1936, b = 6391;
int (*f)(int, int); //Zeiger auf Funktion
while(1) {
char c;
cout <<"(max = 1),(min = 0),(abbruch = sonst)?\n";
cin >> c;
if (c == '1') f = max;
//Zuweisung von max
else if (c == '0') f = min;
//Zuweisung von min
else break;
cout << (*f)(a,b) << '\n';}}
20.11.2000
32
// Zeiger auf Funktionen
//
// Demonstriert Zeiger auf Funktionen
//
#include <iostream.h>
int sum1(int); int sum2(int);
int Summiere(int (*f)(int));
// Funktionsprototypen
main() {
cout << "\terste Summe: "
<< Summiere(sum1) << "\n\n";
cout << "\tzweite Summe: "
<< Summiere(sum2) << endl;
}
Vorl “EINI-I"
Kap 5: Zeiger
int sum1(int n) {
int i, s = 0;
for(i = 0; i < n; i++) s = s + 1;
return s;
}
int sum2(int n) {
int i, s = 0;
for(i = 0; i < n; i++) s = s - 2;
return s;
}
int Summiere(int (*f)(int)) {
int lauf, sum=0;
for (lauf = 0; lauf < 7; lauf++) {
cout
<< "(*f)(" << lauf << ") = "
<< (*f)(lauf) << endl;
sum = sum + (*f)(lauf);
}
return sum;
}
20.11.2000
•Ausführen
34
Vorl “EINI-I"
Kap 5: Zeiger
Mehrdimensionale Felder
• In C++ sind mehrdimensionale Felder möglich
(Details später).
• Beispiel: int matrix [3][7]
– beschreibt eine Matrix mit drei Zeilen und sieben
Spalten, deren Elemente vom Typ int sind,
– Beachte: int matrix [3][7] beschreibt 7 Elemente vom
Typ int matrix [3].
– int matrix [3, 7] ist in C++ syntaktisch nicht legal.
20.11.2000
35
Herunterladen