350 likes | 429 Vues
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I. Kapitel 5 Claudio Moraga; Gisbert Dittrich FBI Unido moraga@cs.uni-dortmund.de. Gliederung Kapitel 5. Zeiger Grundbegriffe Zeiger und Felder Beispiel: Zeichenketten Funktionen als Parameter
E N D
EINI-IEinführung in die Informatik für Naturwissenschaftler und Ingenieure I Kapitel 5 Claudio Moraga; Gisbert Dittrich FBI Unido moraga@cs.uni-dortmund.de
Gliederung Kapitel 5 • Zeiger • Grundbegriffe • Zeiger und Felder • Beispiel: Zeichenketten • Funktionen als Parameter • Mehrdimensionale Felder
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 !
Adresse Wert Name -99 Adresse Wert 123 B Name Variable • Anmerkungen: • Adresse: Eindeutiger Identifikator des Speicherplatzes • Größe des Speicherplatzes abhängig vom Typ der Variablen. • Bildliche Darstellungen: oder auch: Speicherplatz Typ nicht explizit angegeben ! Beispiel:
34567 1230 var Beispiel: -99 1230 B 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: 1230 1230 var 1230 var 34567 34567 1230 1230 var var var Beispiel: Beispiel: -99 -99 -99 -99 -99 -99 var 1230 1230 B B B B B Grundbegriffe Zeiger Gängige alternative Darstellung:
Grundbegriffe Zeiger • SeivarVariablenname: &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).
int k;float *t; Beispiele &kist die Adresse der ganzen Zahl k, tist alsZeiger auf eine Variable vom Typ floatdefiniert,beinhaltet also eine Adresseadreiner derartigen Variablen *t = 17.14speichert damit den Wert 17.14 unter dieser Adresse adr
1 -99 ZeigerA A B ZeigerB ZeigerA 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:
-99 9801 -99 1 9801 -99 9801 -99 ZeigerA ZeigerA ZeigerA ZeigerA A A A A B B B B ZeigerB Beispiele (Forts.) Esgilt *ZeigerA == 1 Nach *ZeigerA = B enthält der Speicherplatz, dessen Adresse ZeigerA ist, den Wert von B, also: Situation nach: B = B * B; • ZeigerB = &B; • *ZeigerA = *ZeigerB;
Merke • *ZeigerA spricht den Speicherplatz an, auf den ZeigerAzeigt: 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
Der Tausch bleibt lokal auf die Funktion beschränkt (wg. call by value) Beispiel Vertausche zwei Werte Was geschieht in Tausch? void Tausch (int a, int b) { int temp; temp = a; a = b; b = temp; }
Beispiel Was geschieht in AdrTausch? void AdrTausch(int *p, int *q) { int temp; temp = *p; *p = *q; *q = temp; } temp nimmt den Inhalt von *p auf Der Inhalt von *q wird als Inhalt von *p gespeichert Der Inhalt von *q ist der in temp gespeicherte Wert
p p p p q q q q 30 -121 30 -121 -121 -121 30 -121 temp temp temp temp 30 30 30 undef Beispiel (Bildchen) temp = *p; *p = *q; *q = temp; Dadurch ist sog. call by reference möglich
// 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 << ", y = " << y << endl; Tausch(x, y); cout << "nach Tausch: x = " << x << ", y = " << y << endl; cout << "oh! (klar: call by value)\n"; cout << "vor AdrTausch: x = " << x << ", y = " << y << endl; AdrTausch(&x, &y); cout << "nach AdrTausch: x = " << x << ", y = " << y << endl; }
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
Felder und Zeiger • Sei deklariertint a[10], x, *pa; • Dann:pa = &a[0] setzt pa alsZeigerauf das ersteElement vona x = *pawürde alsoa[0]nachxkopieren.
Felder und Zeiger • pa+1,..., pa+9 zeigen auf die Elemente a[1],...,a[9] • es gilt also *(pa+i) = a[i]füri = 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.
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.
Felder und Zeiger void strcpy (char nach[ ], char von[ ]) {int i = 0;while ((nach[i] = von[i]) ! = '\0') i++;} Kopiert bekanntlich die Zeichenkette vonin die Zeichenkette nach. Die Zeichenketten werden als Felder dargestellt.
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)++].
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.
Vergleich von Zeichenketten Sind a undb 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 gleichb ist, • strcmp(a, b) ist positiv, wenn a größer als b ist,
Die Zeichenketten werden durchlaufen, solange sie identische Zeichen haben sonst wird die Differenz berechnet tritt dabei das Ende einer Kette auf, sind sie identisch 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;}
Vergleich von Zeichenketten Version mit Feldernint 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
// 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] << " "; }
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
Adr. v1 ... v1[9] v1[0] v1 ... Zur Erinnerung int v1[10]; int *z1; z1 = v1; z1 = z1+1; z1
#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; for (i=0;i<10;i++){ cout << *z1 << " "; z1=z1+1; } cout << '\n'; } // Feldname als 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
Adr. Speicherbereich, wo das Programm der Funktion abgespeichert ist z char (*z)(char, char); Zeiger auf Funktionen; Namen von Funktionen BspFunktion Funktionsname char BspFunktion(char, char); // Funktionsprototyp // Zeiger auf Funktion gleicher Art // Übergabe der Adresse der Funktion z = BspFunktion; cout << (*z)(‘A‘, ‘B‘) << endl; // Funktionsaufruf über z
#include<iostream.h> int max(int x, int y){ int min(int x, int y){ int k ; int k ; if (x > y) k = x; if (x < y) k = x; else k = y; else k = y; return k; } 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';}}
// Zeiger auf Funktionen // // Demonstriert Zeiger auf Funktionen // #include <iostream.h> int sum1(int); int sum2(int); int Summiere(int (*f)(int)); main() { cout << "\terste Summe: " << Summiere(sum1) << "\n\n"; cout << "\tzweite Summe: " << Summiere(sum2) << endl; } // Funktionsprototypen
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; } • Ausführen
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.