Twitter und Facebook-Anbindung
X
Tweet Follow @twitterapi
!!! Anbindung an twitter und facebook öffnen !!!

Wenn Ihnen mein Online-Buch gefällt,
dann bedanken Sie sich doch mit einer kleinen Spende...

9.2.5 Das komplexe Beispiel

9.2.5 Das komplexe Beispiel

Normalerweise beginnt man mit der main Funktion, aber die spielt in diesem Beispiel eine untergeordnete Rolle, von daher werde ich den Quelltext von oben nach unten erklären. Damit Sie wissen, um was es geht, habe ich eine Grafik erstellt, die zum einen zeigt wie es im Arbeitsspeicher aussehen könnte und zum anderen, wie die Zusammenhänge sind.

Veranschaulichung des folgenden Beispieles

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
					
#include <stdio.h>

struct SPunkt3D {
	int iX;			// X - Koordinate
	int iY;			// Y - Koordinate
	int iZ;			// Z - Koordinate
};

struct SDreieck3D {
	SPunkt3D* pPunktA;	// Zeigt auf einen SPunkt3D
	SPunkt3D* pPunktB;	// Zeigt auf einen SPunkt3D
	SPunkt3D* pPunktC;	// Zeigt auf einen SPunkt3D
};
					

Ich denke mal, dass bis hierher noch alles klar sein sollte was passiert. Wenn nicht, dann schlage ich vor, noch einmal Kapitel 8.4 und 9.1 zu lesen.

 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
					
// Kopiert eine SDreieck3D mit all seinen SPunkt3D //////////////////////////(())
// und deren Koordinaten in eine komplett neues SDreieck3D
SDreieck3D* Dreieck3DDeepCopy(SDreieck3D* pSource) {
	// Neues "leeres" Dreieck erzeugen
	SDreieck3D*	pTarget	= new SDreieck3D;



	// "leere" Punkte für das Dreieck erzeugen ///////////////////////////

	// Variante 1
	pTarget->pPunktA	= new SPunkt3D;

	// Variante 2
	(*pTarget).pPunktB	= new SPunkt3D;
	(*pTarget).pPunktC	= new SPunkt3D;



	// Werte Kopieren /////////////////////////////////////////////////////

	// Variante 1
	*(pTarget->pPunktA)	= *(pSource->pPunktA);
	
	// Variante 2
	*((*pTarget).pPunktB)	= *(*pSource).pPunktB;
	
	// Variante 3
	pTarget->pPunktC->iX	= pSource->pPunktC->iX;
	pTarget->pPunktC->iY	= (*pSource->pPunktC).iY;
	pTarget->pPunktC->iZ	= (*(*pSource).pPunktC).iZ;



	return pTarget;
} // Dreieck3DDeepCopy ////////////////////////////////////////////////////////
					

In Dieser Funktion wird diese Dreiecksstruktur mit samt ihren Inhalt in eine neue Variable kopiert und zurückgegeben. Der Einfachheit halber arbeite ich in diesem Beispiel nur mit "new" und "delete". Die fett gedruckten Zeilen sind die Zeilen, welche am einfachsten verständlich sind und deren Stiel Sie sich angewöhnen sollten.

Die Zeile 20 sollte klar sein. Interessanter ist schon Zeile 27 und 31 bzw. 32. Hier werden die einzelnen Punkte des Dreiecks angelegt bzw. es wird Speicherplatz für sie reserviert. Ich habe hier absichtlich zwei verschiedene Schreibweisen gewählt, damit Sie sehen, dass sie äquivalent sind.

Die Zeilen 39 und 42 sind wieder äquivalent. Man ließt dies folgendermaßen: "Die Struktur, die sich dort befindet, worauf "PunktA" zeigt, die zu dem Dreieck gehört, worauf "pSource" zeigt, wird dort hin kopiert, wo "PunktA" hin zeigt, welcher zu dem Dreieck gehört, worauf "pTarget" zeigt".

Alternativ dazu könnte man auch, wie in Zeile 45 bis 47, jeden einzelnen Wert per Hand kopieren. Dies ist zwar einfacher nachzuvollziehen, aber spätestens wenn man dies für eine Struktur mit 100 Elementen machen möchte/muss, wird man davon Abstand nehmen. Auf der rechten Seite des "=" habe ich wieder drei verschiedene Schreibweisen verwendet, welche zwar alle äquivalent sind, sich aber mehr oder weniger gut handhaben bzw. lesen lassen.

 52
 53
 54
 58
 59
 60
 61
 62
 63
					
// Gibt die Koordinaten eines SPunkt3D aus ////////////////////////////////////
void PrintPunkt3D(SPunkt3D* pPunkt) {
	// Variante 1
	printf("%i ", pPunkt->iX);

	// Variante 2
	printf("%i ", (*pPunkt).iY);
	printf("%i\n",(*pPunkt).iZ);
} // PrintPunkt3D /////////////////////////////////////////////////////////////
					

Auch hier sehen Sie wieder wunderschön, die zwei Schreibweisen mit dem "*" und dem "->" und wieder habe ich die leichtere Variante fett hervorgehoben.

 67
 68
 69
 70
 71
 72
 73
 74
 75
					
// Gibt ein SDreieck3D mit all seinen SPunkt3D aus ////////////////////////////
void PrintDreieck3D(SDreieck3D* pDreieck) {
	// Variante 1
	PrintPunkt3D(pDreieck->pPunktA);

	// Variante 2
	PrintPunkt3D((*pDreieck).pPunktB);
	PrintPunkt3D((*pDreieck).pPunktC);
} // PrintDreieck3D ///////////////////////////////////////////////////////////
					

In dieser Funktion möchte ich schon einmal einen kleinen Hinweis dafür geben, wie man ein objektorientiertes Programm aufbaut. Statt eine Ausgabefunktion zu haben, welche sich durch jede der Strukturen hangelt und die einzelnen Elemente ausgibt, verlagert man das Problem auf untergeordnete Funktionen (später auf untergeordnete Objektmethoden). Vereinfacht ausgedrückt heißt das, wenn ich von allen Studenten ein Foto machen soll, frag ich lieber die Studenten ob sie mir ein Bild von sich geben können.

 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
					
// Hauptfunktion des Programms ////////////////////////////////////////////////
int main(int argc, char** argv) {
	// Erzeugen der abstrakten Struktur
	SDreieck3D* pDreieck1	= new SDreieck3D;

	pDreieck1->pPunktA	= new SPunkt3D;
	pDreieck1->pPunktB	= new SPunkt3D;
	pDreieck1->pPunktC	= new SPunkt3D;



	// Befüllen mit Werten

	pDreieck1->pPunktA->iX	= 1;
	pDreieck1->pPunktA->iY	= 2;
	pDreieck1->pPunktA->iZ	= 3;

	pDreieck1->pPunktB->iX	= 4;
	pDreieck1->pPunktB->iY	= 5;
	pDreieck1->pPunktB->iZ	= 6;

	pDreieck1->pPunktC->iX	= 7;
	pDreieck1->pPunktC->iY	= 8;
	pDreieck1->pPunktC->iZ	= 9;



	// Ausgabe

	PrintDreieck3D(pDreieck1);
	printf("\n");

	SDreieck3D* pDreieck2;
	pDreieck2		= Dreieck3DDeepCopy(pDreieck1);

	PrintDreieck3D(pDreieck2);



	// Freigeben des Speichers ////////////////////////////////////////////

	// Variante 1
	delete pDreieck1;
	pDreieck1		= NULL;

	// Variante 2
	delete pDreieck2->pPunktA;
	(*pDreieck2).pPunktA	= NULL;
	
	delete (*pDreieck2).pPunktB;
	(*pDreieck2).pPunktB	= NULL;
	
	delete (*pDreieck2).pPunktC;
	(*pDreieck2).pPunktC	= NULL;

	delete pDreieck2;
	pDreieck2		= NULL;
	
	return 0;
} // main /////////////////////////////////////////////////////////////////////
					

In den Zeilen 82 bis 86 lege ich zunächst das Dreieck an und dann jeweils die Punkte. Zu beachten ist, dass mit der Anlage des Dreiecks noch lange nicht die Punkte existieren, wohl aber die Zeiger (welche vorerst irgendwohin zeigen). In Zeile 92 bis 102 weiße ich den Punkten, mehr oder weniger sinnvolle Werte (Koordinaten) zu, denn mit der Anlage der Punkte existierten zwar bereits die Struktur, allerdings waren auch ihre Werte nicht definiert.

In den Zeilen 108 - 114 wird zuerst das erste Dreieck ausgegeben. Anschließend wird es kopiert und abschließend wird die Kopie zur Kontrolle ausgegeben.

Zuletzt müssen die Dreiecke wieder gelöscht werden. Am einfachsten geht dies wie in Zeile 121. Anschließend sollten Sie, der Form halber, den Zeiger auf "NULL" setzen, da er nun auf einen Bereich zeigt, der ihm nicht mehr gehört. Extrem vorteilhaft an dieser Variante ist, dass das "delete" sehr intelligent ist und nicht nur das Dreieck wieder freigibt, sondern auch die drei Punktstrukturen, mit den Koordinaten, auf welche die Zeiger in der Dreiecksstruktur zeigen. Möchte man, wie in Variante 2, nur einzelne Punkte löschen, weil man die anderen evtl. noch benötigt, ist es extrem wichtig die entsprechenden Zeiger nach dem "delete" auf "NULL" zu setzen. Würde man dies versäumen, gäbe es bei der Freigabe des Dreiecks einen Absturz, da der Zeiger des Punktes ja noch irgendwo hin zeigt, was ihm nicht gehört. Das Programm würde dann versuchen genau diesen Bereich freizugeben und dabei käme es zu einer Speicherzugriffsverletzung. Am Hässlichsten daran ist, dass Sie z.B. ganz weit Oben im Quelltext schon vergessen haben könntet, diesen Zeiger auf "NULL" zu setzen und der Fehler tritt erst bei Programmende auf, was einem nahe legt, den Fehler am Ende des Programms zu suchen, was aber vergeblich wäre.

Achtung! Wenn Sie mit den Funktionen "malloc" und "calloc" arbeitet, müssen Sie die einzelnen Elemente mit "free" freigeben wie in Variante 2. Die Funktion "free" besitzt diesbezüglich keinen Automatismus.

1 2 3
4 5 6
7 8 9

1 2 3
4 5 6
7 8 9
		
Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012