Vorlesung Informatik 1
Vorlesung Informatik 1
(Wintersemester 2024/2025)
Kapitel 9: Adressen und Zeiger
Prof. Dr. Hähner
Julian Linne, Henning Cui, Victor Gerling, Neele Kemper
Universität Augsburg
Fakultät für Angewandte Informatik
11. Dezember 2024
1 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
2 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
3 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.1 (Zahlen vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 21;
int y = 9;
&x
21
&y
9
&a
&b
swap(x, y);
&x
21
&y
9
&a
&b
21
9
int temp = a;
a = b;
b = temp;
&x
21
&y
9
&a
&b
9
21
Werte werden übergeben, Kopien werden vertauscht!
4 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.1 (Zahlen vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 21;
int y = 9;
&x
21
&y
9
&a
&b
swap(x, y);
&x
21
&y
9
&a
&b
21
9
int temp = a;
a = b;
b = temp;
&x
21
&y
9
&a
&b
9
21
Werte werden übergeben, Kopien werden vertauscht!
4 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.1 (Zahlen vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 21;
int y = 9;
&x
21
&y
9
&a
&b
swap(x, y);
&x
21
&y
9
&a
&b
21
9
int temp = a;
a = b;
b = temp;
&x
21
&y
9
&a
&b
9
21
Werte werden übergeben, Kopien werden vertauscht!
4 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.1 (Zahlen vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 21;
int y = 9;
&x
21
&y
9
&a
&b
swap(x, y);
&x
21
&y
9
&a
&b
21
9
int temp = a;
a = b;
b = temp;
&x
21
&y
9
&a
&b
9
21
Werte werden übergeben, Kopien werden vertauscht!
4 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.1 (Zahlen vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 21;
int y = 9;
&x
21
&y
9
&a
&b
swap(x, y);
&x
21
&y
9
&a
&b
21
9
int temp = a;
a = b;
b = temp;
&x
21
&y
9
&a
&b
9
21
Werte werden übergeben, Kopien werden vertauscht!
4 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.2 (Zahlen vertauschen mit Call-by-Reference!)
1
2
3
4
5
6
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
&x
21
&y
9
&a
&b
&temp
&x
&y
temp = *a;
&x
21
&y
9
*a
&a
&x
&y
&b
&temp
21
*a = *b;
&x
9
&y
9
*a
*b
&a
&x
&y
&b
&temp
21
*b = temp;
&x
9
&y
21
*b
&a
&x
&y
&b
&temp
21
Adressen werden übergeben, Originale werden vertauscht!
5 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.2 (Zahlen vertauschen mit Call-by-Reference!)
1
2
3
4
5
6
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
&x
21
&y
9
&a
&b
&temp
&x
&y
temp = *a;
&x
21
&y
9
*a
&a
&x
&y
&b
&temp
21
*a = *b;
&x
9
&y
9
*a
*b
&a
&x
&y
&b
&temp
21
*b = temp;
&x
9
&y
21
*b
&a
&x
&y
&b
&temp
21
Adressen werden übergeben, Originale werden vertauscht!
5 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.2 (Zahlen vertauschen mit Call-by-Reference!)
1
2
3
4
5
6
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
&x
21
&y
9
&a
&b
&temp
&x
&y
temp = *a;
&x
21
&y
9
*a
&a
&x
&y
&b
&temp
21
*a = *b;
&x
9
&y
9
*a
*b
&a
&x
&y
&b
&temp
21
*b = temp;
&x
9
&y
21
*b
&a
&x
&y
&b
&temp
21
Adressen werden übergeben, Originale werden vertauscht!
5 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.2 (Zahlen vertauschen mit Call-by-Reference!)
1
2
3
4
5
6
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
&x
21
&y
9
&a
&b
&temp
&x
&y
temp = *a;
&x
21
&y
9
*a
&a
&x
&y
&b
&temp
21
*a = *b;
&x
9
&y
9
*a
*b
&a
&x
&y
&b
&temp
21
*b = temp;
&x
9
&y
21
*b
&a
&x
&y
&b
&temp
21
Adressen werden übergeben, Originale werden vertauscht!
5 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.2 (Zahlen vertauschen mit Call-by-Reference!)
1
2
3
4
5
6
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
&x
21
&y
9
&a
&b
&temp
&x
&y
temp = *a;
&x
21
&y
9
*a
&a
&x
&y
&b
&temp
21
*a = *b;
&x
9
&y
9
*a
*b
&a
&x
&y
&b
&temp
21
*b = temp;
&x
9
&y
21
*b
&a
&x
&y
&b
&temp
21
Adressen werden übergeben, Originale werden vertauscht!
5 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Einleitung
Beispiel 9.2 (Zahlen vertauschen mit Call-by-Reference!)
1
2
3
4
5
6
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
swap(&x, &y);
&x
21
&y
9
&a
&b
&temp
&x
&y
temp = *a;
&x
21
&y
9
*a
&a
&x
&y
&b
&temp
21
*a = *b;
&x
9
&y
9
*a
*b
&a
&x
&y
&b
&temp
21
*b = temp;
&x
9
&y
21
*b
&a
&x
&y
&b
&temp
21
Adressen werden übergeben, Originale werden vertauscht!
5 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
6 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Zeiger (Pointer)
Datentyp für Adressen
Anwendungsgebiete:
Änderung von Variablenwerten über Call-by-Reference
Dynamische Felder
(Anzahl der Komponenten zur Laufzeit änderbar)
Bisheriger Einsatz:
Bibliotheksfunktionen für Zeichenketten (Datentyp char *),
z.B.:
char *strcpy(char *s, const char *ct)
Übergabe von Variablen, die verändert werden sollen:
scanf("%i", &n);
Zeiger sind getypt, d.h man muss angeben, welchen Datentyp
die Bezugsvariable hat
7 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Zeiger und Bezugsvariable
Ein Zeiger ist eine adresswertige Variable, also eine Variable, deren Wert die
Speicheradresse einer anderen Variable ist:
1
2
3
int x;
int *p;
p = &x;
p speichert die Adresse von x:
&p
&x
&x
p zeigt auf (referenziert) x (oder p ist Zeiger auf x)
x ist Bezugsvariable von p.
Anschauliche Darstellung mit Pfeil (Zeiger):
&p
8 / 107
&x
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Definition 9.3 (Zeiger (Deklaration))
Sei T ein Datentyp. Dann deklariert
T *p;
einen Zeiger p auf T. Es gilt:
p ist adresswertig. Der Typ von p ist T*.
p kann nur solche Adressen speichern, an denen ein Wert vom Typ T gespeichert ist.
Der Bezugstyp von p ist T.
Der Compiler reserviert sizeof(T*) Byte Speicherplatz in aufeinanderfolgenden
Speicherzellen zur Aufnahme der Adresse. Dieser Wert ist implementierungsabhängig
und unabhängig vom Bezugstyp T.
Beispiel
int *px, **ppx;:
px ist ein Zeiger auf int (Einfachzeiger)
ppx ist ein Zeiger auf int* (Doppelzeiger, wird später genauer behandelt)
9 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Doppelzeiger
1
2
3
4
5
int x;
int *q;
int **p;
q = &x;
p = &q;
q speichert Adresse von x, p Adresse von q:
&p
&q
&x
p ist Zeiger auf Zeiger auf int (oder auchDoppelzeiger auf
int)
10 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Notation
Folgende Notationen sind äquivalent:
1
2
3
int *p;
int * p;
int* p;
Allerdings erzeugt int* p, q einen Zeiger und eine
Ganzzahl-Variable
Daher guter Stil: int *p, *q;
11 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Zeiger ohne gültige Adresse
Nicht initialisierte lokale Zeiger zeigen irgendwohin
Nicht initialisierte globale Zeiger zeigen nirgendwohin
Nirgendwo wird durch Wert NULL repräsentiert
Definition 9.4 (Der Wert NULL)
NULL ist eine adresswertige symbolische Konstante. Sie steht dafür, dass ein
Zeiger nirgendwohin zeigt / keine Adresse gespeichert hat. Es gilt:
NULL kann an Zeiger mit beliebigem Bezugstyp zugewiesen werden.
NULL hat eine implementierungsabhängige Codierung.
NULL wird in Bedingungen als falsch/false ausgewertet.
Jeder andere Adresswert wird in Bedingungen als wahr/true ausgewertet.
Darstellung im Speicher:
&p
12 / 107
NULL
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Der Adressoperator &
liefert die Adresse der ersten Speicherzelle des Speicherbereichs einer
Variable
also einen adresswertiger Ausdruck vom Typ T* für eine Variable vom
Datentyp T
Definition 9.5 (Adresswertiger Ausdruck)
Sei T ein Datentyp. Dann sind folgende Ausdrücke adresswertige Ausdrücke mit
Bezugstyp T:
&x
für eine Variable x vom Typ T.
v
für ein Feld v vom Typ T.
q
für einen Zeiger q mit Bezugstyp T.
NULL
13 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Adressen können mit Umwandlungsangabe %p ausgegeben werden
(Ausgabeformat ist plattformabhängig):
1
2
3
4
5
6
7
8
9
10
11
int main() {
double x;
int v[5];
int *p = v;
printf("Adresse von x: %p\n", &x);
printf("Adresse von v: %p\n", v);
printf("Adresse von v: %p\n", p);
printf("Adresse von p: %p\n", &p);
printf("Adresse von v[4]: %p\n", &v[4]);
return 0;
}
Beachte: Adressen sind nicht automatisch ganze Zahlen!
Beachte: Feldnamen und Zeiger sind adresswertig
Beachte: Wert eines Zeigers ̸= Adresse eines Zeigers
14 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Definition 9.6 (Wertzuweisung)
Ist p ein Zeiger und e ein adresswertiger Ausdruck mit demselben
Bezugstyp T, so ist
p = e;
eine Wertzuweisung an p. Man sagt p wird nach e umgebogen.
Beispiel: Umbiegen von Adressen
int x, y;
int *p = &x;
p = &y;
&y
&y
&p
15 / 107
&x
&p
&x
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Definition 9.6 (Wertzuweisung)
Ist p ein Zeiger und e ein adresswertiger Ausdruck mit demselben
Bezugstyp T, so ist
p = e;
eine Wertzuweisung an p. Man sagt p wird nach e umgebogen.
Beispiel: Umbiegen von Adressen
int x, y;
int *p = &x;
p = &y;
&y
&y
&p
15 / 107
&x
&p
&x
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Definition 9.6 (Wertzuweisung)
Ist p ein Zeiger und e ein adresswertiger Ausdruck mit demselben
Bezugstyp T, so ist
p = e;
eine Wertzuweisung an p. Man sagt p wird nach e umgebogen.
Beispiel: Umbiegen von Adressen
int x, y;
int *p = &x;
p = &y;
&y
&y
&p
15 / 107
&x
&p
&x
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Beispiel 9.7 (Strenge Typ-Prüfung)
int x, *px, **ppx;
px = &x; /*px zeigt auf x*/
ppx = &px; /*ppx zeigt auf px*/
ppx = &x; /*Compilerfehler, da ppx kein Zeiger auf
int */
double y;
px = &y; /*Compilerfehler, da px kein Zeiger auf
double */
16 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Mit dem Dereferenzierungsoperator * kann man auf den an einer
Adresse gespeicherten Wert zugreifen.
Beispiel: Dereferenzierungsoperator
1
2
3
int x = 5;
int *p = &x;
++(*p);
Über *p wird Wert der Speicherzelle abgerufen, auf die p zeigt
++(*p) inkrementiert diesen Wert und speichert die Änderung
wieder zurück
Situation danach:
&p
&x
6
*p ist ein Alias (anderer Name) für x.
17 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Definition 9.8 (Dereferenzierung)
Sei e ein adresswertiger Ausdruck mit Bezugstyp T ungleich NULL. Dann ist
*e
eine Variable vom Typ T mit Speicheradresse e. Den Operator * nennt man
Dereferenzierungsoperator.
Achtung
Ein Zeiger auf NULL kann nicht dereferenziert werden. Der Versuch führt zu einem
Programmabbruch.
* ist überladen
18 / 107
1
In Variablen-Deklaration: * zeigt an, dass Zeiger deklariert wird
2
Vor adresswertigem Ausdruck: * dereferenziert den Ausdruck
3
Verwendung zwischen zwei zahlwertigen Ausdrücken:
* multipliziert beide Zahlwerte.
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Definition 9.9 (Der Modifikator const (Teil 2))
Seien T ein Datentyp. Mit der Anweisung
const T *p;
deklariert man einen Zeiger p auf einen konstanten Wert, der
keinen schreibenden Zugriff mittels Dereferenzierung *p
erlaubt. Der Zeiger kann aber umgebogen werden.
T * const p;
deklariert man einen konstanten Zeiger p, der nicht
umgebogen werden kann. Ein schreibender Zugriff mittels
Dereferenzierung *p ist aber möglich.
const T * const p;
deklariert man konstanten Zeiger auf einen konstanten Wert.
19 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger
Beispiel 9.10 (Fehlerquellen (Teil 1))
1
Zeiger wird dereferenziert, zeigt aber irgendwohin (d.h. er wurde nicht
initialisiert) oder nirgendwohin (d.h. er hat den Wert NULL). Beispiel:
int *p;
*p = 5; /*Zugriff in nicht reservierten Bereich */
2
Zeiger wird dereferenziert, aber die referenzierte Variable wurde nicht
initialisiert. Beispiel:
int x;
int *p = &x;
++(*p); /*Rechnen mit undefiniertem Wert */
3
Wertzuweisung an Zeiger mit inkompatiblem Datentyp. Beispiele:
int x = 20;
double *p = &x; /*Compilerfehler */
int *q = x; /*Compilerfehler */
20 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
21 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Zeiger auf ein Feld
v[2]
1
2
int v[3];
int *p = v;
v[1]
&p
v[0]
v
*p entspricht v[0] entspricht *v
p entspricht &v[0] entspricht v
Adressverschiebung
1
2
3
int v[3];
int *p = v;
p = p + 1;
v[2]
v[1]
&p
v[0]
v
*p entspricht v[1] entspricht *(v + 1)
p entspricht &v[1] entspricht (v + 1)
22 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Zeiger auf ein Feld
v[2]
1
2
int v[3];
int *p = v;
v[1]
&p
v[0]
v
*p entspricht v[0] entspricht *v
p entspricht &v[0] entspricht v
Adressverschiebung
1
2
3
int v[3];
int *p = v;
p = p + 1;
v[2]
v[1]
&p
v[0]
v
*p entspricht v[1] entspricht *(v + 1)
p entspricht &v[1] entspricht (v + 1)
22 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Zeiger auf ein Feld
v[2]
1
2
int v[3];
int *p = v;
v[1]
&p
v[0]
v
*p entspricht v[0] entspricht *v
p entspricht &v[0] entspricht v
Adressverschiebung
1
2
3
int v[3];
int *p = v;
p = p + 1;
v[2]
v[1]
&p
v[0]
v
*p entspricht v[1] entspricht *(v + 1)
p entspricht &v[1] entspricht (v + 1)
22 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Zeiger auf ein Feld
v[2]
1
2
int v[3];
int *p = v;
v[1]
&p
v[0]
v
*p entspricht v[0] entspricht *v
p entspricht &v[0] entspricht v
Adressverschiebung
1
2
3
int v[3];
int *p = v;
p = p + 1;
v[2]
v[1]
&p
v[0]
v
*p entspricht v[1] entspricht *(v + 1)
p entspricht &v[1] entspricht (v + 1)
22 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Zeiger auf ein Feld
v[2]
1
2
int v[3];
int *p = v;
v[1]
&p
v[0]
v
*p entspricht v[0] entspricht *v
p entspricht &v[0] entspricht v
Adressverschiebung
1
2
3
int v[3];
int *p = v;
p = p + 1;
v[2]
v[1]
&p
v[0]
v
*p entspricht v[1] entspricht *(v + 1)
p entspricht &v[1] entspricht (v + 1)
22 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Definition 9.11 (Adressverschiebung)
Seien e ein adresswertiger Ausdruck mit Bezugstyp T und Wert a
ungleich NULL und n ein positiver ganzzahliger Ausdruck. Dann ist
e + n
ein adresswertiger Ausdruck mit Bezugstyp T. Sein Wert ist die
Adresse
a + (n * sizeof(T))
Die Adresse a wird um n-mal den Speicherbedarf des Bezugstyps T
verschoben.
Achtung
Vermeide Adressverschiebung in nicht reservierten Bereich
23 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Die Notationen für Zeiger und Felder entsprechen sich:
Definition 9.12 (Notationen für Felder und Zeiger)
Seien T ein Datentyp, v ein Feld vom Typ T, p ein Zeiger mit Bezugstyp
T und n ein positiver ganzzahliger Ausdruck. Dann entsprechen sich die
Notationen in den folgenden Tabellenzeilen:
v + n
*(v + n)
p + n
*(p + n)
&v[n]
v[n]
&p[n]
p[n]
Zeiger und Felder als Eingabeparameter
Die Eingabeparameter T v[] und T *v sind gleichbedeutend.
In beiden Fällen wird eine Adresse übergeben und in der Funktion mit
einem Zeiger gerechnet.
24 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
v+1
p
v
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
v+1
p
v
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
v+1
p
v
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
v+1
p
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
v+1
p
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
v+1
p
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
p
v+1
1
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
p
v+1
1
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
v+2
p
v+1
1
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Mit ++p wird der Zeiger p auf
die Adresse der nächsten
Feldkomponente umgebogen
v+7
v+6
1
2
3
4
5
6
7
8
9
25 / 107
int main() {
int v[8], *p, i;
p = v;
for (i = 0; i < 8;
++i) {
*p = i;
++p;
}
return 0;
}
v+5
v+4
v+3
p
v+2
2
v+1
1
v
0
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Felder
Beispiel 9.13 (Fehlerquellen (Teil 2))
Sei p ein Zeiger mit Bezugstyp T und v ein Feld vom Typ T. Bei der Benutzung von p und v
sollten wir auf Folgendes achten:
Kein Zugriff auf nicht reservierten Speicherbereich:
Beispiel 1:
int v[3];
int *p = v;
p[3] = 4;
Beispiel 2:
int x;
int *p = &x;
++p;
Adressverschiebung nicht mit Operationen auf Dateninhalten verwechseln:
Welchen Inhalt hat v nach folgenden Anweisungen:
int v[3] = {1, 2, 3};
int *p = v;
++(*p);
*(++p) = 4;
Man darf Feldeadressen verschieben, aber nicht umbiegen:
Beispiel:
int v[3];
int *p = v + 1;
++v;
26 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
27 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Schreibweisen für adresswertige Eingabeparameter
Kapitel 6 zu Zeichenketten: Schreibweisen
char v[]
char *v
sind für Eingabeparameter v äquivalent.
Dies gilt nicht nur für Bezugstyp char, sondern für beliebigen Typ T.
Beide Schreibweisen:
v Zeiger, über den man nach Call-by-Reference-Prinzip auf
Dateninhalt an für v übergebener Adresse zugreifen kann
28 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Definition 9.14 (Adresswertige Eingabeparameter)
Sei T ein Datentyp. Hat ein Eingabeparameter v eine der beiden
gleichbedeutenden Formen
T v[]
T *v
so ist v ein Zeiger mit Bezugstyp T und wir können für v Adressen
mit Bezugstyp T übergeben.
Beispiele: Funktionen mit adresswertigen Eingabeparametern
Funktionen zur Verwaltung von Feldern
Bibliotheksfunktionen zur Verwaltung von Zeichenketten
Die Bibliotheksfunktion scanf
29 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
swap Call-by-Value
1
2
3
4
5
6
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
Aufruf für int x, y:
swap(x, y)
Call-by-Value
⇒ Kein Tausch möglich
swap Call-by-Reference
1
2
3
4
5
6
30 / 107
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
Aufruf für int x, y:
swap(&x, &y)
Call-by-Reference
⇒ Tausch klappt
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Übergabe der adresswertigen Konstante NULL
Für adresswertige Eingabeparameter kann adresswertige
Konstante NULL übergeben werden.
Dies führt zu Fehler, wenn in Funktion ohne weitere
Überprüfung Dereferenzierung vorgenommen wird.
Meiste Bibliotheksfunktionen:
keine Überprüfung von übergebenen Werten auf NULL
Gleiches Vorgehen für eigene Funktionen:
Wenn Aufgabenstellung nichts spezifisch erwähnt,
verzichten wir in Funktion auf Überprüfung auf NULL
31 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Beispiel 9.15 (Einlesen einer positiven ganzen Zahl (mit
Call-by-Reference))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
32 / 107
int read_pos_buff_p(int *input)
{
int status;
int c = ’\0’;
status = scanf("%i", input);
if (status == EOF)
return BUFFER_ERROR;
if (status == 0 || *input <= 0 || (c = getchar()) != ’\n
’) {
if (c == EOF || !flush_buff())
return BUFFER_ERROR;
return INVALID_INPUT;
}
return VALID_INPUT;
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Beispiel 9.16 (Eingabefunktion aufrufen (mit Call-by-Reference))
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(void)
{
int input, status;
do {
printf("Gib positive ganze Zahl ein: ");
status = read_pos_buff_p(&input);
if (status == INVALID_INPUT)
printf("Eingabe ungueltig\n");
if (status == BUFFER_ERROR)
return 1;
} while (status != VALID_INPUT);
return 0;
}
Anmerkung: Jetzt sollte klar sein, wieso an scanf Adressen von
Variablen übergeben werden.
33 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Definition 9.17 (Call by Reference - Prinzip (allgemeine Variante))
Sei ein Eingabeparamater p ein Zeiger.
Übergibt man für p die Adresse einer Variable x, so kann man in der
Funktion über die Dereferenzierung *p lesend und schreibend auf x
zugreifen.
Übergibt man für p ein Feld v, so kann man in der Funktion über
Adressverschiebung und Dereferenzierung lesend und schreibend
auf die Feldkomponenten zugreifen.
34 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
Achtung
Übergebenen Adressen unterliegen dem Call-by-Value-Prinzip!
d. h. ein übergebener Zeiger kann in einer Funktion also nicht
umgebogen werden.
Soll ein übergebener Zeiger in einer Funktion umgebogen
werden, muss auf diesen das Call-by-Reference-Prinzip
angewendet werden.
d. h. seine Adresse muss als Doppelzeiger an Funktion
übergeben werden
Details in Unterkapitel 9.7
35 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
1
#include <stdio.h>
3
void increment(int *n);
5
int main() {
int a = 2;
increment(&a);
printf("%i", a);
return 0;
}
6
7
8
9
10
...
...
void increment(int *n) {
++(*n);
}
12
13
14
Stack Frame
1
36 / 107
Variable anlegen und initialisieren
2
Adresse der Variable übergeben
3
Wert über Dereferenzierung
verändern
4
Neuen Wert verwenden
2 von main
(Adressen) (Speicherzellen)
&a
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
1
#include <stdio.h>
3
void increment(int *n);
5
int main() {
int a = 2;
increment(&a);
printf("%i", a);
return 0;
}
6
7
8
9
10
...
Stack Frame
&n
&a von increment
...
11
void increment(int *n) {
++(*n);
}
12
13
14
Stack Frame
1
36 / 107
Variable anlegen und initialisieren
2
Adresse der Variable übergeben
3
Wert über Dereferenzierung
verändern
4
Neuen Wert verwenden
2 von main
(Adressen) (Speicherzellen)
&a
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
1
#include <stdio.h>
3
void increment(int *n);
5
int main() {
int a = 2;
increment(&a);
printf("%i", a);
return 0;
}
6
7
8
9
10
...
Stack Frame
&n
&a von increment
...
void increment(int *n) {
++(*n);
}
12
13
14
Stack Frame
1
36 / 107
Variable anlegen und initialisieren
2
Adresse der Variable übergeben
3
Wert über Dereferenzierung
verändern
4
Neuen Wert verwenden
3 von main
2
(Adressen) (Speicherzellen)
&a
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Call by Reference
1
#include <stdio.h>
3
void increment(int *n);
5
int main() {
int a = 2;
increment(&a);
printf("%i", a);
return 0;
}
6
7
8
9
10
...
...
void increment(int *n) {
++(*n);
}
12
13
14
Stack Frame
1
36 / 107
Variable anlegen und initialisieren
2
Adresse der Variable übergeben
3
Wert über Dereferenzierung
verändern
4
Neuen Wert verwenden
3 von main
(Adressen) (Speicherzellen)
&a
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
37 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Wie wir in den Übungen schon gesehen haben, können wir auch Zeichenketten als
Feld repräsentieren
char w[] = "Joerg";
38 / 107
w+6
w+5
'\0'
w+4
'g'
w+3
'r'
w+2
'e'
w+1
'o'
w
'J'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Wie wir in den Übungen schon gesehen haben, können wir auch Zeichenketten als
Feld repräsentieren und ebenso wie in Kapitel 9.3 einen Zeiger auf dieses Feld
zeigen lassen.
w+6
char w[] = "Joerg";
char *p = w;
p
38 / 107
w+5
'\0'
w+4
'g'
w+3
'r'
w+2
'e'
w+1
'o'
w
'J'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Wie wir in den Übungen schon gesehen haben, können wir auch Zeichenketten als
Feld repräsentieren und ebenso wie in Kapitel 9.3 einen Zeiger auf dieses Feld
zeigen lassen.
w+6
char w[] = "Joerg";
char *p = w;
In diesem Beispiel haben
w und p dieselbe Adresse
als Wert.
38 / 107
p
w+5
'\0'
w+4
'g'
w+3
'r'
w+2
'e'
w+1
'o'
w
'J'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Wie wir in den Übungen schon gesehen haben, können wir auch Zeichenketten als
Feld repräsentieren und ebenso wie in Kapitel 9.3 einen Zeiger auf dieses Feld
zeigen lassen.
w+6
char w[] = "Joerg";
char *p = w;
p
w+5
'\0'
w+4
'g'
w+3
'r'
w+2
'e'
w+1
'o'
w
'J'
Frage:
Welche Zeichenkette repräsentiert p nach der Anweisung p = w + 1;?
38 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Wie wir in den Übungen schon gesehen haben, können wir auch Zeichenketten als
Feld repräsentieren und ebenso wie in Kapitel 9.3 einen Zeiger auf dieses Feld
zeigen lassen.
w+6
char w[] = "Joerg";
char *p = w;
p = w + 1;
p
w+5
'\0'
w+4
'g'
w+3
'r'
w+2
'e'
w+1
'o'
w
'J'
Frage:
Welche Zeichenkette repräsentiert p nach der Anweisung p = w + 1;?
Antwort:
p repräsentiert nach dieser Anweisung die Zeichenkette “oerg”.
38 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.18 (Zeichenkette (Version mit Zeigern))
Sei e ein adresswertiger Ausdruck mit Bezugstyp char. Dann deklariert
char *p;
eine (noch undefinierte) Zeichenkette p. Nach der Wertzuweisung:
p = e;
repräsentiert p die im Speicher abgelegte Zeichenkette ab der Speicherzelle mit
Adresse e bis zur ersten Speicherzelle mit dem Inhalt '\0'.
Eine solche Wertzuweisung kann auch in der Deklaration erfolgen.
Einige Möglichkeiten zur Deklaration und Wertzuweisung von Zeichenketten:
39 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.18 (Zeichenkette (Version mit Zeigern))
Sei e ein adresswertiger Ausdruck mit Bezugstyp char. Dann deklariert
char *p;
eine (noch undefinierte) Zeichenkette p. Nach der Wertzuweisung:
p = e;
repräsentiert p die im Speicher abgelegte Zeichenkette ab der Speicherzelle mit
Adresse e bis zur ersten Speicherzelle mit dem Inhalt '\0'.
Eine solche Wertzuweisung kann auch in der Deklaration erfolgen.
Einige Möglichkeiten zur Deklaration und Wertzuweisung von Zeichenketten:
char *p;
p = "Joerg";
39 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.18 (Zeichenkette (Version mit Zeigern))
Sei e ein adresswertiger Ausdruck mit Bezugstyp char. Dann deklariert
char *p;
eine (noch undefinierte) Zeichenkette p. Nach der Wertzuweisung:
p = e;
repräsentiert p die im Speicher abgelegte Zeichenkette ab der Speicherzelle mit
Adresse e bis zur ersten Speicherzelle mit dem Inhalt '\0'.
Eine solche Wertzuweisung kann auch in der Deklaration erfolgen.
Einige Möglichkeiten zur Deklaration und Wertzuweisung von Zeichenketten:
char *p;
p = "Joerg";
39 / 107
char *p = "Joerg";
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.18 (Zeichenkette (Version mit Zeigern))
Sei e ein adresswertiger Ausdruck mit Bezugstyp char. Dann deklariert
char *p;
eine (noch undefinierte) Zeichenkette p. Nach der Wertzuweisung:
p = e;
repräsentiert p die im Speicher abgelegte Zeichenkette ab der Speicherzelle mit
Adresse e bis zur ersten Speicherzelle mit dem Inhalt '\0'.
Eine solche Wertzuweisung kann auch in der Deklaration erfolgen.
Einige Möglichkeiten zur Deklaration und Wertzuweisung von Zeichenketten:
char *p;
p = "Joerg";
39 / 107
char *p = "Joerg";
char w[] = "Joerg";
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.18 (Zeichenkette (Version mit Zeigern))
Sei e ein adresswertiger Ausdruck mit Bezugstyp char. Dann deklariert
char *p;
eine (noch undefinierte) Zeichenkette p. Nach der Wertzuweisung:
p = e;
repräsentiert p die im Speicher abgelegte Zeichenkette ab der Speicherzelle mit
Adresse e bis zur ersten Speicherzelle mit dem Inhalt '\0'.
Eine solche Wertzuweisung kann auch in der Deklaration erfolgen.
Einige Möglichkeiten zur Deklaration und Wertzuweisung von Zeichenketten:
char *p;
p = "Joerg";
char *p = "Joerg";
char w[] = "Joerg";
Achtung: Zeichenketten im linken und mittigen Beispiel sind konstant!
⇒ Diese können nicht verändert werden!
39 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Achtung: Wertzuweisung zu einer konstanten Zeichenkette
char *p;
p = "Bye";
p[0] = ’R’;
Dieser Code funktioniert nicht,
da p auf ein Feld voller Konstanten zeigt,
die nicht verändert werden dürfen.
Besonderheiten bei Wertzuweisung in Deklarationen
char w[] = "Hallo";
w ist konstante Adresse einer
w+7
Zeichenkette
(++w; erzeugt Compilerfehler)
w+5
\0
w+4
o
char *p = "Hallo";
p ist variabler Zeiger auf
w+3
l
w+2
l
w+1
a
w
H
konstante Zeichenkette
(p[1] = ’e’; lässt
Programm abstürzen)
40 / 107
w+6
p
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Achtung: Wertzuweisung zu einer konstanten Zeichenkette
char *p;
p = "Bye";
p[0] = ’R’;
Dieser Code funktioniert nicht,
da p auf ein Feld voller Konstanten zeigt,
die nicht verändert werden dürfen.
Besonderheiten bei Wertzuweisung in Deklarationen
char w[] = "Hallo";
w ist konstante Adresse einer
w+7
Zeichenkette
(++w; erzeugt Compilerfehler)
w+5
\0
w+4
o
char *p = "Hallo";
p ist variabler Zeiger auf
w+3
l
w+2
l
w+1
a
w
H
konstante Zeichenkette
(p[1] = ’e’; lässt
Programm abstürzen)
40 / 107
w+6
p
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.19 (Zeichenkettenkonstante)
Eine Zeichenkettenkonstante besteht aus einem Wort aus ASCII-Zeichen
a1 . . . an , das in Anführungszeichen eingeschlossen ist:
"a1 . . . an "
Ihre Zeichen liegen im Speicher in aufeinanderfolgenden Speicherzellen. Ihr Wert
ist eine Adresse mit Bezugstyp char, und zwar die Adresse der Speicherzelle mit
dem ersten Zeichen.
Beispiel 9.20 (Zeichenkette mit Adressverschiebung durchlaufen)
Unsere eigene, sehr kompakte Version von strcpy
Wir verwenden Adressverschiebung und Dereferenzierung
Wir verzichten zunächst auf Rückgabewert von strcpy
1
2
3
41 / 107
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.19 (Zeichenkettenkonstante)
Eine Zeichenkettenkonstante besteht aus einem Wort aus ASCII-Zeichen
a1 . . . an , das in Anführungszeichen eingeschlossen ist:
"a1 . . . an "
Ihre Zeichen liegen im Speicher in aufeinanderfolgenden Speicherzellen. Ihr Wert
ist eine Adresse mit Bezugstyp char, und zwar die Adresse der Speicherzelle mit
dem ersten Zeichen.
Beispiel 9.20 (Zeichenkette mit Adressverschiebung durchlaufen)
Unsere eigene, sehr kompakte Version von strcpy
Wir verwenden Adressverschiebung und Dereferenzierung
Wir verzichten zunächst auf Rückgabewert von strcpy
1
2
3
41 / 107
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
'p' v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
'p' v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
'p' v+1
w+1
'p'
w
'S'
'S' v
42 / 107
s
ct
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Schauen wir uns Beispiel 9.20 etwas genauer an.
1
2
3
void my_strcpy(char *s, const char *ct) {
while ((*(s++) = *(ct++)) != ’\0’) {}
}
v+6
w+6
v+5
w+5
'\0'
v+4
w+4
's'
v+3
w+3
's'
v+2
w+2
'a'
'p' v+1
w+1
'p'
w
'S'
'S' v
s
ct
Wiederholung bis *ct == '\0'
42 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Entwicklung einer Funktion char * split(char *w, char c)
Sucht c in w und überschreibt c mit \0'
Gibt Zeiger auf Zeichenkette hinter c zurück
Gibt NULL zurück, wenn c nicht in w enthalten ist
Beispiel 9.21 (Adresswertige Rückgabe)
1
2
3
4
5
6
7
8
9
10
11
43 / 107
char* split(char *w, char c)
{
int i = 0;
while (w[i] != c && w[i] != ’\0’)
++i;
if (w[i] == c) {
w[i] = ’\0’;
return &w[i + 1];
} else
return NULL;
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Entwicklung einer Funktion char * split(char *w, char c)
Sucht c in w und überschreibt c mit \0'
Gibt Zeiger auf Zeichenkette hinter c zurück
Gibt NULL zurück, wenn c nicht in w enthalten ist
Beispiel 9.21 (Adresswertige Rückgabe)
1
2
3
4
5
6
7
8
9
10
11
43 / 107
char* split(char *w, char c)
{
int i = 0;
while (w[i] != c && w[i] != ’\0’)
++i;
if (w[i] == c) {
w[i] = ’\0’;
return &w[i + 1];
} else
return NULL;
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Definition 9.22 (Adressen als Rückgabewerte)
Sei T ein Datentyp. Ist
T*
der Rückgabetyp einer Funktion, so gibt die Funktion eine Adresse mit Bezugstyp T
oder NULL zurück. Ist
void*
der Rückgabetyp einer Funktion, so gibt die Funktion eine Adresse mit beliebigem
Bezugstyp oder NULL zurück. Vor deren Dereferenzierung muss die Adresse einem
getypten Zeiger zugewiesen werden.
NULL wird üblicherweise zur Anzeige eines aufgetretenen Fehlers verwendet und
kann beim Aufrufer zur Fehlerbehandlung verwendet werden.
Einige Zeichenkettenfunktionen aus string.h haben den Rückgabetyp char*.
char *strcpy(s, ct)
char *strcat(s, ct)
...
44 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Zeiger als Rückgabetyp
Beispiele aus string.h (unvollständig)
char * strcpy(char * s, const char * ct): liefert Adresse
von s
char *strchr(const char * cs, int c): liefert Adresse des
ersten Vorkommens von c in cs bzw. NULL, falls c in cs nicht vorkommt:
S6
\0
S5
6
S4
(Rückgabe) S3
1
S2
2
S1
1
.
strchr("12.16",’.’); liefert Adresse S3 – also Zeichenkette ".16"!
char *strtok(char *s1, const char *s2):
zerlegt s1 in Zeichenketten, die durch Zeichen aus s2 begrenzt sind;
wiederholter Aufruf für selbe Zeichenkette mit NULL für s1
45 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Zeiger und Zeichenketten
Achtung
Niemals eine Adresse eines freigegebenen Speicherbereichs zurückgeben!
Beispiel 9.23 (Murks mit Rückgabeadressen)
1
2
3
4
6
7
8
9
10
int* murks() {
int n = 5;
return &n;
}
int main() {
int *p;
p = murks();
printf("%d\n", *p); /*Zugriff auf undefinierten Bereich*/
}
n liegt im Function Stack Frame von murks
Sobald murks verlassen wird, wird Speicher von n freigegeben
p enthält dann Adresse von Speicher mit undefiniertem Inhalt
46 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
47 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Größe von Feldern
Bisher bei Verwendung von Feldern:
Statische Festlegung der Anzahl der Feldkomponenten
d.h. zum Zeitpunkt des Compilierens
⇒ Problem: Wie groß wählt man Anzahl?
Folgen:
Zur Laufzeit maximal so viele Komponenten verwendbar
Verschwendung von Speicherplatz wenn man weniger
Komponenten braucht
Verbesserung: Dynamische Felder
Möglichkeit, die Anzahl der Feldkomponenten dynamisch (d.h. zur
Laufzeit des Programms) festzulegen
48 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Varianten für dynamische Speicherverwaltung
1
Einmaliges Anlegen eines dynamischen Feldes mit bestimmter Länge, die
erst zur Laufzeit feststeht: malloc, calloc
2
Anpassen der Länge eines bereits angelegten dynamischen Feldes, wenn
sich zur Laufzeit Speicherbedarf ändert: realloc
Vorteile
Feldgröße nicht von vornherein durch Obergrenze beschränkt
Keine Verschwendung von Speicherplatz
Technische Umsetzung
Reservierung des Speicherplatzes auf dem Heap
o.g. Funktionen geben Adresse des reservierten Speichers zurück
Wenn kein Speicher reserviert werden konnte, Rückgabe NULL
49 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Einmaliges Anlegen eines dynamischen Feldes für eine Zeichenkette
char *p = malloc((size + 1)* sizeof(char));
if (p != NULL){ /*Dereferenzierung von p */}
&p
...
p
p+1
p+2
p+size
malloc erwartet Größe des zu reservierenden Speicherbereichs in Byte
Berechnung der Größe des Speicherbereichs:
Anzahl der Komponenten · Größe eines Elements
Bestimmung der Größe eines Elements systemunabhängig via sizeof
malloc gibt Adresse des reservierten Speicherbereichs bzw. NULL zurück
50 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Wie lange ist Reservierung gültig?
Im Heap reservierter Speicher ist unabhängig von Gültigkeitsbereichen
Speicher muss mit free wieder freigegeben werden, wenn er nicht mehr gebraucht
wird → Speicherleck wenn Speicher nicht mehr freigegeben wird
Speicherreservierung in Funktion möglich, die Zeiger auf Speicher zurückgibt
Beispiel 9.24 (Dynamisches Kopieren von Zeichenketten)
1
2
3
4
5
6
7
char* string_d_copy(const char *ct)
{
char *copy = malloc((strlen(ct) + 1) * sizeof(char));
if (copy == NULL)
return NULL;
return strcpy(copy, ct);
}
Zeile 3: Benötigten Speicherplatz dynamisch reservieren (Platz für ’\0’ nicht
vergessen!)
Zeilen 4 – 5: Fehlerbehandlung
Zeile 6: Kopieren und Adresse zurückgeben
51 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Beispiel: Dynamisches Kopieren von Zeichenketten
1
2
3
4
5
6
7
8
9
10
11
int main(void)
{
char * error = string_d_copy("Error");
if (error == NULL) {
printf("Speicherfehler\n");
return EXIT_FAILURE;
}
printf("Zeichenkette: %s\n", error);
free(error);
return EXIT_SUCCESS;
}
Zeile 3: Zeichenkette kopieren – in string_d_copy dynamisch reservierter
Speicherplatz kann in main weiter benutzt werden
Zeilen 4 – 7: Fehlerbehandlung
Zeile 9:
Freigabe des in string_d_copy dynamisch reservierten Speicherbereichs
52 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Definition 9.25 (malloc-Funktion)
Die Funktion
void* malloc(size_t size)
versucht einen zusammenhängenden Speicherbereich im Heap in der Größe
von size Byte zu reservieren, hat den Rückgabewert NULL im Fehlerfall, und gibt
die Adresse der ersten Speicherzelle des reservierten Bereichs im Erfolgsfall
zurück. Es gilt:
Die Rückgabeadresse hat keinen Bezugstyp und muss vor Deferenzierung
einem Zeiger mit Bezugstyp zugewiesen werden.
Der Speicherbereich ist nicht initialisiert.
Der Speicherbereich wird nicht automatisch wieder freigegeben – dies muss
durch einen separaten Funktionsaufruf zu free erfolgen.
Alternative calloc: Unterschiede zu malloc
Initialisiert Speicherbereich zusätzlich mit 0en
Zwei Parameter: 1. Anzahl der Elemente, 2. Größe eines Elements
53 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Systemunabhängige Festlegung der Speichergröße mit sizeof
Größe des benötigten Speicherplatzes festlegen:
für Grund-Datentyp T: malloc(sizeof(T))
für Feld der Länge n vom Typ T: malloc(n * sizeof(T))
Beispiel: Dynamische Speicherreservierung im Speicher
int *p;
p = malloc(8 * sizeof(int));
H8
Es werden 32 Byte Speicherplatz im
Heap reserviert
H7
p zeigt auf diesen Speicherplatz
H6
Der Wert von p ist die Adresse H1
H5
&p und H1 sind unterschiedliche
H4
Adressen
H3
p[0] ist eine int-Variable an
Adresse H1
p[1] ist eine int-Variable an
Adresse H2
54 / 107
H2
&p
H1
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Schema zur Anwendung dynamischer Speicherreservierung
1
Speicherreservierung mit Wertzuweisung an Zeiger mit
Bezugstyp T (automatische Umwandlung nach T*)
T *p = malloc(n * sizeof(T))
2
Fehlerbehandlung
(ein Zeiger auf NULL kann nicht dereferenziert werden!):
if (p == NULL){ /*Fehlerbehandlung */}
3
Verwaltung des dynamischen Feldes über Dereferenzierung des
Zeigers:
p[i] = /*Wertzuweisung */
4
Nach Benutzung Freigabe des reservierten Speicherplatzes:
free(p)
55 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Dynamisch im Heap reservierter Speicherplatz muss explizit
wieder freigegeben werden. Dies geschieht nicht automatisch.
Definition 9.26 (free-Funktion)
Die Funktion
free(void *p)
gibt einen dynamisch reservierten Speicherbereich, auf den p zeigt,
wieder frei. Hat p den Wert NULL, so hat die Funktion keine
Auswirkungen.
Falls p nicht auf dynamisch reservierten Speicher zeigt, ist das
Verhalten undefiniert.
Es kann sogar zum Programmabsturz kommen!
56 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Veränderung der Größe des reservierten Speichers mit realloc
Wenn zu wenig/zu viel Speicher reserviert wurde:
Anpassung der Größe mit realloc
Gilt nur für Speicher im Heap!
(mit malloc, calloc oder realloc reserviert)
Parameter von realloc:
1. Zeiger auf Speicher, 2. neue Größe
Rückgabe:
Adresse des neuen Speicherbereichs bzw. NULL im Fehlerfall
57 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Beispiel 9.27 (Dynamisches Verlängern von Zeichenketten)
1
2
3
4
5
6
7
char* string_d_cat(char *s, const char *ct)
{
s = realloc(s, (strlen(s) + strlen(ct) + 1) * sizeof(char)
);
if (s == NULL)
return NULL;
return strcat(s, ct);
}
Zeile 3: Anpassung des Speicherplatzes
(es wird Platz für s, ct und ’\0’ benötigt)
Zeilen 4 – 5: Fehlerbehandlung
Zeile 6: Verlängern und Adresse zurückgeben
58 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Beispiel: Dynamisches Verlängern von Zeichenketten
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(void)
{
char * w;
char * error = string_d_copy("Error");
w = string_d_cat(error, ": Invalid Input");
if (w == NULL) {
printf("Speicherfehler\n");
free(error);
return EXIT_FAILURE;
}
error = w;
printf("Zeichenkette: %s\n", error);
free(error);
return EXIT_SUCCESS;
}
Zeile 5: Verlängerung Zeichenkette – der in Funktion string_d_cat dynamisch
reservierte Speicherplatz kann in main weiter benutzt werden
Zeile 13: Freigabe des in string_d_cat dynamisch reservierten Speicherbereichs
59 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Beispiel: Dynamisches Verlängern von Zeichenketten
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(void)
{
char * w;
char * error = string_d_copy("Error");
w = string_d_cat(error, ": Invalid Input");
if (w == NULL) {
printf("Speicherfehler\n");
free(error);
return EXIT_FAILURE;
}
error = w;
printf("Zeichenkette: %s\n", error);
free(error);
return EXIT_SUCCESS;
}
Zeilen 5 – 11: Fehlerbehandlung, inklusive Freigabe von nicht mehr benötigtem Speicherplatz
Zeile 5: Adresse kann nicht an error zugewiesen werden, da error dann im Fehlerfall mit
NULL überschrieben würde. So entstünde ein Speicherleck, da keine Speicherfreigabe über
error mehr möglich wäre (wie sie Zeile 8 erfolgt).
60 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Definition 9.28 (realloc-Funktion)
Die Funktion
void* realloc(void *p, size_t size)
versucht einen dynamisch reservierten Speicherbereich im Heap, auf den p zeigt,
auf eine Größe von size Byte anzupassen (zu verkleinern oder zu vergrößern).
Es gilt:
realloc hat den Rückgabewert NULL im Fehlerfall. In diesem Fall bleiben p
und der von p referenzierte Bereich unverändert.
realloc gibt die Adresse der ersten Speicherzelle des angepassten
Bereichs im Erfolgsfall zurück.
Im Falle einer Vergrößerung des Speicherbereichs werden zusätzliche
Speicherzellen nicht initialisiert. Vorhandene Daten bleiben erhalten.
Im Falle einer Verkleinerung des Speicherbereichs bleibt der Inhalt im
verkleinerten Speicherbereich erhalten.
Hat p den Wert NULL, so verhält sich realloc wie malloc.
Hat size den Wert 0, so verhält sich realloc wie free.
61 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Speichervergrößerung
Bei Vergrößerung des Speicherbereichs wird zuerst versucht, bisherigen
Bereich zu vergrößern.
Falls dies nicht möglich ist, wird an anderer Stelle neuer Bereich
ausreichender Größe gesucht.
Im Erfolgsfall wird bisheriger Inhalt dorthin kopiert (alter Speicherbereich wird
dann freigegeben). Inhalt des zusätzlichen Bereichs ist undefiniert.
Speicherverkleinerung
Der Inhalt im verkleinerten Speicherbereich bleibt erhalten.
Achtung
Falls man versucht, nicht dynamisch reservierten Speicherbereich zu vergrößern
oder zu verkleinern, ist das Verhalten von realloc undefiniert und es kann zum
Programmabsturz kommen.
62 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Definition 9.29 (Speicherleck)
Ein Speicherleck ist ein dynamisch reservierter und nicht wieder
freigegebener Speicherbereich, der nicht mehr benutzt wird.
Speicherlecks sind unbedingt zu vermeiden, da durch sie
Speicherplatz verschwendet wird. Im schlimmsten Fall können sie
zum Programmabbruch führen, da für ein Programm nicht mehr
genügend Speicher vorhanden ist.
Dynamisch reservierten Speicherplatz immer freigeben!
Wenn man Speicher immer nur reserviert und nicht wieder freigibt,
wenn er nicht mehr benötigt wird, kann er schließlich voll werden,
was zu schweren Fehlern führt
63 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Dynamische Speicherverwaltung
Unterscheidung Statische und Dynamische Felder
Statische Felder
Konstante maximale Anzahl der Komponenten
Speicherreservierung durch Felddeklaration
Speicherung im Stack
Zugriff nur im eigenen Gültigkeitsbereich
Automatische Speicherfreigabe am Ende des Gültigkeitsbereichs
Dynamische Felder
Variable Anzahl der Komponenten
Speicherreservierung durch Funktionsaufruf mit Fehlerüberprüfung
Speicherung im Heap
Zugriff im ganzen Programm
Speicherfreigabe durch den Programmierer
Dynamische Speicherverwaltung
Speicherreservierung mit malloc oder calloc
Änderung der Größe des reservierten Speichers mit realloc
Freigabe des reservierten Speicher erforderlich mit free
64 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
65 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.30 (Zeichenketten vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap_string(char *a, char *b)
{
char *temp = a;
a = b;
b = temp;
}
Originale
66 / 107
swap_string
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.30 (Zeichenketten vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap_string(char *a, char *b)
{
char *temp = a;
a = b;
b = temp;
}
Kopie
66 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.30 (Zeichenketten vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap_string(char *a, char *b)
{
char *temp = a;
a = b;
b = temp;
}
Kopie
66 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.30 (Zeichenketten vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap_string(char *a, char *b)
{
char *temp = a;
a = b;
b = temp;
}
Vertauschung
66 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.30 (Zeichenketten vertauschen mit Call-by-Value?)
1
2
3
4
5
6
void swap_string(char *a, char *b)
{
char *temp = a;
a = b;
b = temp;
}
Originale unverändert!
66 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.31 (Zeichenketten vertauschen mit Call-by-Reference)
1
2
3
4
5
6
void swap_string(char **a, char **b)
{
char *temp = *a;
*a = *b;
*b = temp;
}
Originale
67 / 107
swap_string
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.31 (Zeichenketten vertauschen mit Call-by-Reference)
1
2
3
4
5
6
void swap_string(char **a, char **b)
{
char *temp = *a;
*a = *b;
*b = temp;
}
Kopie der Adressen
67 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.31 (Zeichenketten vertauschen mit Call-by-Reference)
1
2
3
4
5
6
void swap_string(char **a, char **b)
{
char *temp = *a;
*a = *b;
*b = temp;
}
Kopie der Adressen
67 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.31 (Zeichenketten vertauschen mit Call-by-Reference)
1
2
3
4
5
6
void swap_string(char **a, char **b)
{
char *temp = *a;
*a = *b;
*b = temp;
}
Vertauschung
67 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.31 (Zeichenketten vertauschen mit Call-by-Reference)
1
2
3
4
5
6
void swap_string(char **a, char **b)
{
char *temp = *a;
*a = *b;
*b = temp;
}
Originale vertauscht!
67 / 107
&x
&y
&a
&b
'x'
'\0'
'y'
'\0'
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
T **p;
p ist ein Zeiger auf T* (T* ist also der Bezugstyp)
*p ist ein Zeiger auf T.
**p ist eine Variable vom Typ T.
p
*p
**p
Programmvariable
Vor der Benutzung der Programmvariablen p muss man folgendes
machen:
Speicher für *p reservieren (statisch oder dynamisch) und
initialisieren
Speicher für **p reservieren (statisch oder dynamisch) und
initialisieren
68 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Definition 9.32 (strtod-Funktion)
Die Funktion
double strtod(const char *s, char **endp)
wandelt den Anfang der Zeichenkette s in double um. Dabei
werden Zwischenraumzeichen am Anfang ignoriert.
Speichert einen Zeiger auf einen nicht umgewandelten Rest
der Zeichenkette s bei *endp, falls endp nicht NULL ist
69 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel: strtod-Funktion
Beim Aufruf von strdod wird für endp Adresse eines Einfach-Zeigers übergeben:
char *rest;
double x = strtod("123.5ab", &rest);
&rest
'1'
&x
'.'
'5'
'a'
'b'
'\0'
Abarbeitung strtod:
70 / 107
1
Erkennung Zahl
2
Zeiger auf nicht umgewandelten Rest
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel: strtod-Funktion
Beim Aufruf von strdod wird für endp Adresse eines Einfach-Zeigers übergeben:
char *rest;
double x = strtod("123.5ab", &rest);
&rest
'1'
&x
'.'
'5'
'a'
'b'
'\0'
Abarbeitung strtod:
70 / 107
1
Erkennung Zahl
2
Zeiger auf nicht umgewandelten Rest
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel: strtod-Funktion
Beim Aufruf von strdod wird für endp Adresse eines Einfach-Zeigers übergeben:
char *rest;
double x = strtod("123.5ab", &rest);
1.5
&x
&rest
'1'
'.'
'5'
'a'
'b'
'\0'
Abarbeitung strtod:
70 / 107
1
Erkennung Zahl
2
Zeiger auf nicht umgewandelten Rest
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel: strtod-Funktion
Beim Aufruf von strdod wird für endp Adresse eines Einfach-Zeigers übergeben:
char *rest;
double x = strtod("123.5ab", &rest);
1.5
&x
&rest
'1'
'.'
'5'
'a'
'b'
'\0'
Abarbeitung strtod:
70 / 107
1
Erkennung Zahl
2
Zeiger auf nicht umgewandelten Rest
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel: strtod-Funktion
Beim Aufruf von strdod wird für endp Adresse eines Einfach-Zeigers übergeben:
char *rest;
double x = strtod("123.5ab", &rest);
1.5
&x
&rest
'1'
'.'
'5'
'a'
'b'
'\0'
Abarbeitung strtod:
70 / 107
1
Erkennung Zahl
2
Zeiger auf nicht umgewandelten Rest
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Mehrfachzeiger
Beispiel 9.33 (string_d_copy mit Doppelzeiger)
Implementieren Sie zur Übung eine Funktion
int string_dd_copy(char **s, const char *ct)
die die Zeichenkette ct kopiert und dafür an der Adresse *s dynamisch
Speicherplatz reserviert. Im Fehlerfall soll die Funktion 0 zurückgeben, sonst 1.
Wir verwenden den Rückgabewert also nur noch, um den Erfolgs- vom Fehlerfall zu
unterscheiden. Beim Aufruf übergeben wir für s die Adresse einer Zeichenkette:
char *copy;
int status = string_dd_copy(&copy, "Max");
if (status == 0){ /*Fehlerbehandlung */}
71 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
72 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Eindimensionale Felder nicht für jede Anwendung geeignet
Beispiel: Temperatur für jeden Tag speichern
Speichere für jeden Tag eines Jahres Mittagstemperatur:
Bisher: Speichern von Temperaturen als Folge (ti )1≤i ≤365 von
Werten für jeden Tag
Aber: Für Zugriff über Datumsangabe ungeeignet
→ Was war die Temperatur am 1. Mai?
Bessere Darstellung: Speichere Temperaturen als Tabelle mit
12 Zeilen und 31 Spalten.
⇒ Temperatur lässt sich über jeweilige Monatszeile und
Tagesspalte ablesen
Gesucht: Geeignete Datenstruktur in C für T .
73 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Eindimensionale Felder nicht für jede Anwendung geeignet
Weiteres Beispiel: Lösung linearer Gleichungssysteme
Löse lineares Gleichungssystem A · x = b mit Vektoren x = (x1 , . . . , xn ) und
b = (b1 , . . . , bm ) und Matrix A = (ai ,j )1≤i ≤m,1≤j ≤n
Gesucht: Geeignete Datenstruktur in C für A.
Matrixoperationen werden dann als Funktionen in eigener
Übersetzungseinheit implementiert
Zweidimensionale Datenstrukturen in C
Verschiedene zweidimensionale Datenstrukturen für Vektoren und
Matrizen
Mit statischer oder dynamischer Speicherreservierung
Auswahl hängt von Erfordernissen der Anwendung ab
74 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Notationen für Vektoren
Vektor
(Zeilen-)Vektor v der Länge n:
v = (vj )1≤j ≤n oder v = (v1 , . . . , vn )
Durch Transponierung Umwandlung in
Spaltenvektor (rechts)
v1
..
t
v = .
vn
Einträge vj heißen Koeffizienten von v
Operationen auf Vektoren
Addition: v + w := (vj + wj )1≤j ≤n
Skalarmultiplikation: x · v := (x · vj )1≤j ≤n
Vektormultiplikation: v · w t := ∑nj=1 vj · wj
75 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Notationen für Matrizen
Matrix
Wir betrachten eine Matrix A mit m Zeilen und n Spalten
Notation A = (ai ,j )1≤i ≤m,1≤j ≤n oder
a1,1
..
A= .
am,1
...
..
.
...
a1,n
..
.
am,n
Einträge ai ,j heißen Koeffizienten von A
Zeilen und Spalten von A sind jeweils Vektoren
A hat m Zeilen (a1,j )1≤j ≤n , . . . , (am,j )1≤j ≤n
A hat n Spalten (ai ,1 )1≤i ≤m , . . . , (ai ,n )1≤i ≤m
76 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Notationen für Matrizen
Operationen für Matrizen
Addition: A + B := (ai ,j + bi ,j )1≤i ≤m,1≤j ≤n
Skalarmultiplikation: x · A := (x · ai ,j )1≤i ≤m,1≤j ≤n
Vektormultiplikation:
a1,1
..
A · v := .
am,1
77 / 107
...
..
.
...
a1,n
v1
∑nj=1 vj · a1,j
.. ..
..
.
. · . :=
n
am,n
vn
∑j =1 vj · am,j
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Folgende 4 Datenstrukturen sind zur Darstellung von Matrizen
geeignet:
Deklaration zweidimensionaler Datenstrukturen
1
Zweidimensionale (statische) Felder:
T v[N1 ][N2 ];
2
Zeiger auf ein Feld:
T (*p)[N2 ];
3
(Statisches) Feld von Zeigern:
T *v[N1 ];
4
Doppelzeiger:
T **p;
78 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
79 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
T v[N][M];
Hier wird ein Feld deklariert, dessen Komponenten wieder Felder sind (lies die
Deklaration ausgehend von v von innen nach außen):
v ist ein Feld mit N Komponenten.
Der Compiler reserviert hierfür N * M * sizeof(T) Byte Speicherplatz.
v[i] sind jeweils Felder mit M Komponenten (0 ≤ i ≤ N − 1)
v[i][j] sind Variablen vom Typ T (0 ≤ i ≤ N − 1, 0 ≤ j ≤ M − 1)
v[N-1][M-1]
...
v[N-1][1]
v[N-1] v[N-1][0]
... ...
v[1][M-1]
...
v[1][1]
v[1] v[1][0]
v[0][M-1]
...
v[0][1]
v, v[0] v[0][0]
Programmvariable
80 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Definition 9.34 (2-dimensionales Feld)
Seien N1 und N2 positive ganzzahlige Konstanten, T ein Datentyp und k1,1 , . . . ,
kn1 ,n2 Konstanten vom Typ T mit n1 ≤ N1 und n2 ≤ N2 . Dann deklariert T
v[N1 ][N2 ]; (Version ohne Initialisierung)
T v[N1 ][N2 ] = {{k1,1 , ..., k1,n2 }, ..., {kn1 ,1 , ..., kn1 ,n2 }};
(Version mit Initialisierung) ein 2-dimensionales Feld v mit den Dimensionen N1
und N2 . Es gilt:
Der Compiler reserviert N1 * N2 * sizeof(T) Byte Speicherplatz in
aufeinanderfolgenden Speicherzellen.
v ist ein Feld mit N1 Komponenten und damit eine adresswertige Konstante.
Ihr Wert ist die Adresse der ersten Speicherzelle des reservierten
Speicherbereichs. Der Bezugstyp der Adresse ist ein Feld vom Typ T mit N2
Komponenten.
Die i-te Komponente v[i-1] ist ein Feld mit N2 Komponenten vom Typ
T. v[i-1] ist also auch eine adresswertige Konstante. Ihr Wert ist die
Adresse v + (i-1) (diese hat den Bezugstyp T).
Auf die j-te Komponente von v[i-1] greift man mit v[i-1][j-1] zu.
v[i-1][j-1] ist eine Variable vom Typ T.
Mit Initialisierung bekommt v[i-1][j-1] den Wert ki ,j .
81 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Repräsentiere eine Matrix durch ein zweidimensionales Feld
T m[N][M];
m[N-1][M-1]
...
m[N-1][1]
m[N-1] m[N-1][0]
N-te Zeile
... ...
m[1][M-1]
...
m[1][1]
m[1] m[1][0]
m[0][M-1]
...
m[0][1]
m, m[0] m[0][0]
2. Zeile
1. Zeile
Programmvariable
m[i] ist die Adresse der i+1-ten Zeile
m[i][j] ist der j+1-te Eintrag der i+1-ten Zeile
Der Speicherbereich wird statisch reserviert und kann zur Laufzeit auch nur teilweise
benutzt werden
82 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Bei Übergabe eines zweidimensionalen Feldes als Eingabeparameter wird
nur die zweite Dimension angegeben (wird für die Adressverschiebung
benötigt: Wo beginnt die nächste Zeile?)
Aber nicht die erste Dimension (denn nicht alle reservierten Zeilen müssen
benutzt werden)
Beispiel 9.35 (Funktionen für Matrizen definieren)
1
2
3
4
5
6
7
void matrix_init(int m[][MAX_COLUMNS], int ze, int sp) {
int i,j;
for(i = 0; i < ze; ++i) {
for(j = 0; j < sp; ++j)
m[i][j] = rand() % 1000;
}
}
Der Zugriff auf die Matrixeinträge einer Matrix m erfolgt über m[i][j] (in
verschachtelten Schleifen mit zwei Zählvariablen)
83 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Ein zweidimensionales Feld muss vor der Benutzung statisch angelegt werden.
Für die verwendeten Dimensionen verwendet man symbolische Konstanten
(hier: MAX_ROWS, MAX_COLUMNS)
Diese Dimensionen stellen Obergrenzen für die Benutzung zur Laufzeit dar
Beispielprogramm: Matrix statisch anlegen, mit Zufallszahlen initialisieren und
ausgeben (die Matrix-Dimensionen können innerhalb der Obergrenzen MAX_ROWS,
MAX_COLUMNS auch zur Laufzeit eingegeben werden)
Beispiel 9.36 (Funktionen für Matrizen aufrufen)
1
2
3
4
5
6
7
84 / 107
int main() {
srand(time(NULL));
int matrix[MAX_ROWS][MAX_COLUMNS];
matrix_init(matrix,5,7);
matrix_print(matrix,5,7);
return 0;
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Definition 9.37 (2-dimensionale Felder als Eingabeparameter)
Seien T und R Datentypen und N eine positive ganzzahlige Konstante. Mit der
Anweisung
R f(T a[][N2 ], size_t rows, size_t cols, ...);
deklariert man eine Funktion f mit
einem Eingabeparameter a für die Übergabe eines 2-dimensionalen
Feldes, dessen Komponenten Felder mit N2 Komponenten vom Typ T sind.
Eingabeparametern rows und cols für die Übergabe der Anzahlen der
tatsächlich benutzten Komponenten des übergebenen Feldes.
In der Funktion kann mit der Notation a[i][j] lesend und schreibend auf die
Komponenten des übergebenen Feldes zugegriffen werden.
Ein 2-dimensionales Feld v vom Typ T mit den Dimensionen n und m (mit m ≤ N2 )
übergibt man mit
f(v, n, m, ...)
an einen Funktionsaufruf von f.
85 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
86 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Variante 2: Zeiger auf ein Feld
Deklaration: T (*p)[N2 ];
p ist ein Zeiger und kein Feld
Bezugstyp: Feld mit N2 Komponenten
a[N1 -1][N2 -1]
...
a[N1 -1]
a[N1 -1][0]
...
a[0][N2 -1]
...
&a
87 / 107
a[0]
v
a[0][0]
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Definition 9.38 (Zeiger auf ein Feld)
Seien N2 eine positive ganzzahlige Konstanten und T ein Datentyp. Dann
deklariert
T (*p)[N2 ];
einen Zeiger p auf ein Feld mit N2 Komponenten vom Typ T. Es gilt:
Der Bezugstyp von p sind Felder mit N2 Komponenten vom Typ T.
Dereferenzieren wir, so ist *p damit ein solches Feld.
Der Compiler reserviert Speicherplatz für eine Adresse (vom
Bezugstyp). Um über p eine Matrix zu verwalten, muss man für diese
zuerst statisch oder dynamisch Speicherplatz reservieren.
Der Ausdruck p[i-1] ist ein Feld mit N2 Komponenten vom Typ T
(für jedes positive ganzzahlige i).
Auf die j-te Komponente von p[i-1] greift man mit p[i-1][j-1]
zu.
88 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Eine Adresse kann einem Zeiger zugewiesen werden (in einer Wertzuweisung oder
Parameterübergabe), wenn ihr Typ dem Bezugstyp des Zeigers entspricht.
Einem Zeiger T (*p)[N] kann zugewiesen werden (Bezugstyp: T[N]):
der Wert eines anderen Zeigers auf ein Feld gleicher Länge:
T (*q)[N];
p = q;
eine Feldadresse eines 2-dimensionalen Feldes vom Typ T[N]:
T v[M][N];
p = v;
Folgerung
Die Eingabeparameter T (*p)[N] und T v[][N] sind gleichwertig. In beiden
Fällen wird in der Funktion mit einem Zeiger auf ein Feld mit N Komponenten
gerechnet.
89 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
90 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Variante 3: (Statisches) Feld
von Zeigern
Deklaration:
T *v[N1 ];
...
v ist ein Feld mit N1
Komponenten
v
Jede Feldkomponente
ist ein Zeiger auf T
91 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
v[rows-1][cols-1]
...
Variante 3: (Statisches) Feld
von Zeigern
v[rows-1][0]
Deklaration:
T *v[N1 ];
...
v ist ein Feld mit N1
Komponenten
Jede Feldkomponente
ist ein Zeiger auf T
v[1][cols-1]
...
v
v[1][0]
v[0][cols-1]
...
v[0][0]
91 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Beispiel 9.39 (Dynamisch Speicherplatz für Matrizen anlegen
(Feld von Zeigern))
1
2
3
4
5
6
7
8
9
10
11
12
13
int matrix_create(int *v[], int rows, int cols)
{
int i;
for (i = 0; i < rows; ++i) {
v[i] = malloc(cols * sizeof(int));
if (v[i] == NULL) {
for (k = 0; k < i; ++k)
free(v[k]);
return 0;
}
}
return 1;
}
Zeile 4: Für jede Zeile Speicher reservieren
Zeilen 5 – 9:
Im Fehlerfall alle bisher reservierten Zeilen freigeben
92 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Beispiel 9.40 (Speicherplatz für Matrizen freigeben
(Feld von Zeigern))
1
2
3
4
5
void matrix_destroy(int *v[], int rows) {
int i;
for (i = 0; i < rows; ++i)
free(v[i]);
}
Zeilen 3 – 4:
Alle Zeilen durchlaufen und freigeben
93 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Definition 9.41 (Feld von Zeigern)
Seien N1 eine positive ganzzahlige Konstanten und T ein Datentyp. Dann
deklariert
T* v[N1 ];
einen Feld v von Zeigern auf T. Es gilt:
Der Compiler reserviert N1 * sizeof(T*) Speicherplatz in
aufeinanderfolgenden Speicherzellen.
v ist ein Feld mit N1 Komponenten und eine adresswertige Konstante.
Die i-te Komponente v[i-1] ist ein Zeiger auf T.
Über v[i-1] kann man ein (1-dimensionles) Feld vom Typ T
verwalten. Dazu muss man zuerst statisch oder dynamisch
Speicherplatz für ein solches Feld reservieren.
Auf die j-te Komponente von v[i-1] greift man mit v[i-1][j-1]
zu.
94 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Gemeinsamer Zugriff auf verschiedene Variablen
Gleiche Operationen auf verschiedenen Variablen, indem Variablen über
gemeinsames Feld von Zeigern erreichbar gemacht werden
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
95 / 107
int *zeigerfeld[LAENGE_ZEIGERFELD];
int a = 1;
int b = 2;
int c = 3;
int i;
zeigerfeld[0] = &a;
zeigerfeld[1] = &b;
zeigerfeld[2] = &c;
for (i = 0; i < LAENGE_ZEIGERFELD; i++) {
*zeigerfeld[i] *= 10;
}
for (i = 0; i < LAENGE_ZEIGERFELD; i++) {
printf("Variable %c: ", i+97);
printf("%i\n", *zeigerfeld[i]);
}
return 0;
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
96 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Variante 4: Doppelzeiger
Deklaration: T **p;
p zeigt auf Feld von Zeigern mit einem Eintrag pro Zeile
Dieses zeigt wiederum auf Felder mit einem Eintrag pro Spalte
a[rows-1][cols-1]
...
a[rows-1][0]
a+(rows-1)
...
&a
a+1
a
v
a[1][cols-1]
...
a[1][0]
a[0][cols-1]
...
a[0][0]
97 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Eine Adresse kann einem Zeiger zugewiesen werden (in einer Wertzuweisung oder
Parameterübergabe), wenn ihr Typ dem Bezugstyp des Zeigers entspricht.
Einem Zeiger T** p kann zugewiesen werden (Bezugstyp: T*):
der Wert eines anderen Zeigers auf T*:
T** q;
p = q;
die Adresse eines Zeigers auf T :
T* q;
p = &q;
eine Feldadresse vom Typ T* (d.h. ein Feld von Zeigern):
T* v[N];
p = v;
Folgerung
Die Eingabeparameter T** p und T* v[] sind gleichwertig. In beiden Fällen wird
in der Funktion mit einem Doppelzeiger gerechnet.
98 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
2-dimensionale Felder mit dynamischer Speicherreservierung
1
Doppelzeiger für Zugriff
2
Speicher für erste Dimension (Zeilen) reservieren
Als T*-Feld: Speichert jeweils Zeiger auf Feld für Zeile
Speicher für zweite Dimension (Spalten) reservieren
Als T-Feld: Speichert die Werte der Spalten in dieser Zeile
3
Bei Fehler: bisher reservierten Speicher freigeben
→ Alle bereits reservierten Zeilen + T*-Feld!
99 / 107
4
Im 2-dimensionalen Feld arbeiten
5
Speicher der zweiten Dimension freigeben
6
Speicher der ersten Dimension freigeben
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Repräsentiere eine Matrix durch einen Doppelzeiger
T **m;
m
Programmvariable
...
m[1]
m[0]
...
m[1][1]
m[1][0]
...
m[0][1]
m[0][0]
m[i] ist ein Zeiger auf die i+1-te Zeile (in der
Feld-Schreibweise)
m[i][j] ist der j+1-te Eintrag der i+1-ten Zeile (in der
Feld-Schreibweise)
Die Speicherbereiche, auf die die Zeiger m und m[i] zeigen,
werden dynamisch reserviert
100 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Repräsentiere eine Matrix durch einen Doppelzeiger
T **m;
Verwaltungsfunktionen:
Speicherplatz reservieren für Matrix mit ze Zeilen und sp Spalten:
int **matrix_create(int ze, int sp);
Liefert Zeiger auf den reservierten Speicherplatz im Erfolgsfall und NULL
sonst
Für Matrix reservierten Speicherplatz wieder freigeben:
void matrix_destroy(int **m, int ze);
Gibt für die Zeiger m und m[i] reservierte Speicherbereiche wieder frei
Matrixeinträge ausgeben:
void matrix_print(int **m, int ze, int sp);
Übergebe die Matrix als Doppelzeiger und die Dimensionen der Matrix
Matrixeinträge mit Zufallszahlen initialisieren:
void matrix_init(int **m, int ze, int sp);
Übergebe die Matrix als Doppelzeiger und die Dimensionen der Matrix
101 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Beispiel 9.42 (Dynamisch Speicherplatz für Matrizen anlegen (Doppelzeiger))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int **matrix_create(int ze, int sp) { /* Speicher res. */
int **m, i, k;
m = malloc(ze * sizeof(int*));
if (!m) return NULL; /* Fehlerfall */
for (i = 0; i < ze; ++i) {
m[i] = malloc(sp * sizeof(int));
if (!m[i]) { /* Fehlerfall */
for (k = 0; k < i; ++k)
free(m[k]); /* Speicherlecks vermeiden */
free(m); /* Speicherlecks vermeiden */
return NULL;
}
}
return m; /* Erfolgsfall */
}
Schlägt eine der Speicherreservierungen fehl, muss vorher erfolgreich reservierter
Speicher wieder freigegeben werden (Zeilen 7 - 10)
102 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int **matrix_create(int ze, int sp) { /* Speicher res. */
int **m, i, k;
m = malloc(ze * sizeof(int*));
if (!m) return NULL; /* Fehlerfall */
for (i = 0; i < ze; ++i) {
m[i] = malloc(sp * sizeof(int));
if (!m[i]) { /* Fehlerfall */
for (k = 0; k < i; ++k)
free(m[k]); /* Speicherlecks vermeiden */
free(m); /* Speicherlecks vermeiden */
return NULL;
}
}
return m; /* Erfolgsfall */
}
m
Programmvariable
(Zeile 2)
103 / 107
...
m[1]
m[0]
(Zeile 3)
...
m[1][1]
m[1][0]
(Zeile 6)
...
m[0][1]
m[0][0]
(Zeile 6)
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Beispielprogramm: Matrix dynamisch anlegen, mit Zufallszahlen initialisieren und
ausgeben (die Matrix-Dimensionen können auch zur Laufzeit eingegeben werden)
1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
9
104 / 107
void matrix_init(int **m, int ze, int sp) {
int i,j;
for(i = 0; i < ze; ++i) {
for(j = 0; j < sp; ++j)
m[i][j] = rand() % 1000;
}
}
int main() {
srand(time(NULL));
int **matrix = matrix_create(8,10); /* Speicher res. */
if (!matrix) return 1; /* Fehlerfall */
matrix_init(matrix,8,10); /* Initialisieren */
matrix_print(matrix,8,10); /* Ausgeben */
matrix_destroy(matrix,8); /* Speicher freigeben */
return 0;
}
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Beispiel 9.43 (Speicherplatz für Matrizen freigeben (Doppelzeiger))
1
2
3
4
5
6
void matrix_destroy(int **m, int ze) { /*Speicher freig.*/
int i;
for(i = 0; i < ze; ++i)
free(m[i]);
free(m);
}
Vor der Freigabe des Speicherbereichs, auf den m zeigt (Zeile 5), müssen die
Speicherbereiche, auf die die Zeiger m[i] zeigen (Zeile 4), freigegeben werden
Sonst kann man auf diese nicht mehr zugreifen und es entstehen Speicherlecks
m
Programmvariable
...
m[1]
m[0]
(Zeile 5)
105 / 107
...
m[1][1]
m[1][0]
(Zeile 4)
...
m[0][1]
m[0][0]
(Zeile 4)
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
9. Adressen und Zeiger
9.1 Einleitung
9.2 Zeiger
9.3 Zeiger und Felder
9.4 Call by Reference
9.5 Zeiger und Zeichenketten
9.6 Dynamische Speicherverwaltung
9.7 Mehrfachzeiger
9.8 Matrizenrechnung
Variante 1: 2-dimensionale statische Felder
Variante 2: Zeiger auf ein Feld
Variante 3: (Statisches) Feld von Zeigern
Variante 4: Doppelzeiger
Variante 5: Einfachzeiger
106 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
Adressen und Zeiger
Vorlesung Informatik 1
Matrizenrechnung
Repräsentiere eine Matrix mit n Zeilen und k Spalten durch einen Einfach-Zeiger
auf Speicherbereich mit n * k * sizeof(T) Byte
T *m = malloc(n * k * sizeof(T));
m[(n-1) * k + (k-1)]
...
m[(n-1) * k + 1]
m[(n-1) * k + 0]
n-te Zeile (k Einträge)
...
m[1 * k + (k-1)]
...
m[1 * k + 1]
m[1 * k + 0]
m[0 * k + (k-1)]
...
m[0 * k + 1]
m[0 * k + 0]
m
2. Zeile (k Einträge)
1. Zeile (k Einträge)
Programmvariable
m[i * k + j] ist der j+1-te Eintrag der i+1-ten Zeile
Der Speicherbereich wird dynamisch reserviert
107 / 107
Prof. Dr. Jörg Hähner, Universität Augsburg
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )