Post on 06-Apr-2015
Einführung in die Programmiersprache C
4.Tag
Institut für Mathematische Optimierung -
Technische Universität Braunschweig
Zeiger bei Matrizen
Es kann nötig sein, eine Matrix zu kopieren, um bei Veränderungen noch auf die Originaldaten zurückgreifen zu können. Das geht nicht so:
double **mat,**copy;...copy=mat;
Hier werden nicht die Inhalte der Matrix kopiert, sondern nur der Zeiger auf den Beginn des Speicherbereichs. Veränderungen von Einträgen von mat führen auch zur Veränderung der Einträge von copy.
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zeiger bei Matrizen
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Andere Operationen
Sollen hingegen zwei Matrizen oder Zeilen von Matrizen getauscht werden, so können auch nur die Zeiger getauscht werden:
double **mat; **copy, **temp1, *temp2;...temp1 = mat;mat = copy;copy = temp1;...temp2 = mat[17];mat[17] = mat[43];mat[43] = temp2;
Lösung
Die Inhalte müssen elementweise kopiert werden. Dabei müssen die Matrixdimensionen jeweils gleich sein und Speicher für beide Matrizen besorgt werden:
double **mat, **copy;
mat=alloc_matrix(zeilen,spalten);copy=alloc_matrix(zeilen,spalten);for...
for...copy[i][j]=mat[i][j];
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Speicher für Matrizen freigeben
Der für Matrizen besorgte Speicher muss iterativ wieder freigegeben werden:
void freeintmatrix(int **matrix, int zeile){ int i;
for(i=0;i<zeile;i++) free(matrix[i]);
free(matrix);}
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Aufgabe
Schreiben Sie zwei Funktionen, die dynamisch Speicher reservieren und diesen zurückgeben. Eine Funktion soll dabei Speicher für ein eindimensionales Feld (array) freigeben und die andere Speicher für ein mehrdimensionales Feld (Typ Matrix). Implementieren Sie außerdem eine Funktion, die den für mehrdimensionale Felder besorgten Speicher wieder freigibt.Überlegen Sie hierbei welche Parameter Sie den Funktionen übergeben müssen.Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zeigerarithmetik
Eine weitere Möglichkeit mit Zeigern zu arbeiten ist die Zeigerarithmetik. Zur Verdeutlichung hier ein kleines Beispiel. Bei dem Feld:
int a[]={1,2,3};zeigt a auf a[0] und a+1 zeigt auf a[1]. Statt
scanf(" %d",&a[1]);kann man also
scanf(" %d",a+1);schreiben, um den zweiten Wert des arrays zu ändern.
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zeigerarithmetik
Konsequenterweise gibt es auch a++, ++a, a-- und --a. Die Anweisungen:
int *p;int a[]={1,2,3,4};for (p=a; *p != 4; p++) printf(" %d", *p);
sind äquivalent zu:int i;int a[]={1,2,3,4};for (i=0; a[i] != 4; i++) printf(" %d", a[i]);
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zeigerarithmetik bei Matrizen
Bei Matrizen sieht das noch etwas komplizierter aus:double **mat;mat=alloc_matrix(2,2);mat[0][0]=1.0;mat[0][1]=2.0;mat[1][0]=3.0;mat[1][1]=4.0;
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zeigerarithmetik bei Matrizen
printf("mat[0][0] = %f\n",mat[0][0]);printf("mat[1][1] = %f\n",mat[1][1]);printf("**mat = %f\n",**mat);printf("*(*mat+1) = %f\n",*(*mat+1));printf("*(*(mat+1) ) = %f\n",*(*(mat+1)));printf("*(*(mat+1)+1) = %f\n",*(*(mat+1)+1));
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Aufgabe
Schreiben Sie eine 3x3 Matrix sukzessive mit den Werten 1,2,3,… voll. D.h. Zeile 1 enthält die Einträge 1,2,3, Zeile 2 die Einträge 4,5,6 und Zeile 3 die Einträge 7,8,9. Geben Sie darauf per Zeigerarithmetik die komplette Matrix am Bildschirm aus.
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Strukturen
Objekte verschiedenen Datentyps können zu einem neuenDatentyp kombiniert werden. Beispiel:struct student {char[100] vorname;char[100] nachname;int matrikelnummer;char[100] studienfach;};Definiert den Datentyp struct student.
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Aufbau einer Struktur
struct Name {
Deklaration1;Deklaration2;...DeklarationN;
};
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zugriff auf Strukturelemente/* Definition von "Koordinaten": */struct Koordinaten{
double x;double y;double z;
};/* Deklaration einer Variable dieses Typs: */struct Koordinaten p;
p.x=3.0;p.y=p.x; /* Zugriff auf die Elemente: */p.z=0;Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zuweisungen und Vergleiche
Man kann Strukturen als Ganzes zuweisen, aber Vergleiche sind nicht als Ganzes erlaubt.
struct Koordinaten p1, p2;p1 = p2; /* Zuweisung */if (p1==p2) ... /* <-- Fehler */
Die if-Bedingung ist in dieser Form nicht erlaubt, sondern es müssten alle Komponenten der Struktur gesondert auf Gleichheit überprüft werden.
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Strukturen und Zeiger
Eine Struktur darf sich nicht selbst als Element enthalten, aber sie darf einen Zeiger auf eine Variable vom Typ der eigenen Struktur enthalten.
struct knoten{
int wert;struct knoten *naechster;
};Die Struktur knoten enthält einen Zeiger auf eine Struktur vom Typ knoten. Darüberhinaus können in jeder Struktur andere Strukturen verwendet werden.Institut für Mathematische Optimierung – Technische Universität Braunschweig
Zugriff auf Strukturzeigerinhalte
Auf die einzelnen Datenelemente einer Struktur zuzugreifen, deren Adresse man durch eine Zeigervariable kennt, kann auf verschiedene Arten geschehen:
struct knoten *p;
(*p).wert=0; /* 1. Art */p[0].wert=0; /* 2. Art */p->wert=0; /* 3. Art */
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Strukturen als ParameterStrukturen können als Parameter verwendet werden:
struct knoten {
int wert;struct knoten *naechster;
};void ausgabewert(struct knoten p) {
printf("%i\n",p.wert);}
Dabei werden sie als Kopie übergeben (call-by-value).
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Besser
…struct knoten *p;
p->wert=0; // p[0].wert=0; oder (*p).wert=0;ausgabewert(p);…void ausgabewert(struct knoten *p) {
printf("%i\n",p->wert);}
Verwendung von einem Zeiger (call-by-reference).
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Strukturen als RückgabewertStrukturen können auch als Rückgabewert verwendet werden:
struct knoten {int wert;struct knoten *naechster;};
struct knoten generate() {struct knoten result;...return result;}Institut für Mathematische Optimierung – Technische Universität Braunschweig
Eigene Datentypen
Mit dem Schlüsselwort typedef kann man Datentypen mit einem eigenen Namen versehen. Mit:struct knoten_s{int wert;struct knoten_s *naechster;};typedef struct knoten_s knoten;kann man eine Variable vom Typ struct knoten_s jetzt durch knoten p; deklarieren und das mitunter lästige struct weglassen.Institut für Mathematische Optimierung – Technische Universität Braunschweig
Verkürzte Schreibweise
• Verkürzte Schreibweise durch Kombination der Deklarationen:typedef struct knoten_s{int wert;struct knoten_s *naechster;} knoten;
• Andere Anwendungsbeispiele für typedef:typedef unsigned int uint;typedef double real;/* typedef float real; */
(leichter Wechsel zwischen float und double)Institut für Mathematische Optimierung – Technische Universität Braunschweig
Einfach verkettete Listen
Mit Hilfe von Strukturen lassen sich Listen erzeugen. Jedes Element (Knoten) der Liste hat einen Zeiger auf seinen Nachfolger. Das erste Element wird Kopf genannt, das letzte Element hat NULL als Nachfolger:
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Elemente einfügen
Um ein neues Element nach einem Listenelement einzufügen, müssen die Nachfolgezeiger geändert werden:
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Elemente einfügen
Die Anweisungen dazu sind:
knoten *kopf, *vorgaenger, *neuer_knoten;
neuer_knoten->naechster=vorgaenger->naechster;vorgaenger->naechster=neuer_knoten;
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Elemente am Kopf einfügen
Folgende Anweisungen hängen ein neues Element am Kopf an die Liste an:
knoten *kopf, *neuer_knoten;neuer_knoten->naechster = kopf;kopf = neuer_knoten;
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Elemente entfernen
Beim Entfernen eines Elementes muss wiederum der Zeiger des Vorgängerelementes geändert werden.
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Elemente entfernen
Die Anweisungen dazu sind:knoten *kopf, *vorgaenger, *zu_loeschen;vorgaenger->naechster = zu_loeschen->naechster;free(zu_loeschen);oder beim Entfernen des ersten Elements:kopf = zu_loeschen->naechster;free(zu_loeschen);
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Durchlaufen einer Liste
Listen können z.B. mittels For-Schleife durchlaufen werden:knoten *p;int max_wert;...max_wert = head->wert;for (p=head; p!=NULL; p=p->naechster) {if (p->wert > max_wert)
max_wert = p->wert;}
Institut für Mathematische Optimierung – Technische Universität Braunschweig
RekursionRuft eine Funktion sich selbst auf, so wird das Rekursion genannt. Diese muss immer eine Abbruchbedingung haben. Beispiel:
#include <stdio.h>void printnumbers(int a) {
printf("%d ",a);if (a > 0)
printnumbers(a-1);}int main() {
printnumbers(10);return 0;
}
Schlechtes Beispiel für eine Rekursion
Berechnung der Fakultät einer Zahl:unsigned int fakultaet(unsigned int);
int main() {
printf("%u\n",fakultaet(5));return 0;
}
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Schlechtes Beispiel für eine Rekursion
unsigned int fakultaet(unsigned int n){
if(n>0)return n*fakultaet(n-1); /* n!=n*(n-1)!*/
elsereturn 1; /* 0!=1 */
}
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Besser: Iterative Lösung
int main() {
int n=5,i,fakultaet=1;
for(i=2;i<=n;i++)fakultaet*=i; /* fakultaet=fakultaet*i; */
printf("%d\n",fakultaet);return 0;
}Institut für Mathematische Optimierung – Technische Universität Braunschweig
Bäume
Analog zu Listenlassen sich auch baumartige Daten-strukturen erzeugen:
Institut für Mathematische Optimierung – Technische Universität Braunschweig
Traversierung von BäumenSollen alle Knoten eines Baumes abgearbeitet werden, wird oft Rekursion verwendet. Beispiel:
double maxwert; // globale Variable
void traverse(knoten* r) {
if (r->wert > maxwert) maxwert = r->wert;
if (r->rechts!=NULL)traverse(r->rechts);
if (r->links!=NULL)traverse(r->links);
}
Nochmal ohne globale Variableint traverse(knoten* r) {
double h;double maxwert;
maxwert = r->wert;if (r->rechts!=NULL) { h = traverse(r->rechts);
if (h > maxwert) h = maxwert; }
if (r->links!=NULL) { h = traverse(r->links);
if (h > maxwert) h = maxwert; }
}