Zusammengesetzte Datentypen -Arrays und Strukturen Informatik für Elektrotechnik und Informationstechnik Benedict Reuschling [email protected] Hochschule Darmstadt Fachbereich Informatik WS 2013/14 Zuletzt aktualisiert: 25.11.2013, 12:55 Uhr Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Überblick 1 Arrays Mehrdimensionale Arrays 2 Strukturen 2 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Motivation Bis jetzt hatten wir Variablen so kennengelernt, dass diese nur einen Wert speichern können. Sobald ein neuer Wert zugewiesen wird, überschreibt dieser den vorherigen Wert. Für viele Anwendungsfälle genügt es, einen Wert pro Variable zu speichern. Was aber tun, wenn mehrere Werte des gleichen Typs eingelesen werden sollen? Beispielsweise sollen Messwerte (von einem Sensor) eingelesen und berechnet werden. int messwert1, int messwert2, int messwert3, ... Es muss eine bessere Lösung geben als vorsorglich eine grosse Menge von einzelnen Variablen zu initialisieren, von denen evtl. gar nicht alle gebraucht werden (Verschwendung von Speicherressourcen). 3 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Arrays deklarieren Um mehrere Elemente des gleichen Datentyps (z.B. int, void ist nicht erlaubt) zusammenzufassen gibt es in C++ Arrays (Felder). Der Zugriff auf die Elemente des Arrays erfolgt über einen Index (0, 1, 2, . . . ). Arrays sind statische Objekte, was bedeutet, dass die Anzahl der abzuspeichernden Elemente vorher vom Programmierer festgelegt sein muss. Allgemein werden Arrays folgendermassen deklariert: datentyp name[AnzahlElemente]; Beispiel: double meinArray[5]; Graphische Darstellung: 0 Wert1 1 Wert2 2 Wert3 3 Wert4 4 Wert5 4 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Index des Arrays festlegen Der Index und die Grösse des Arrays müssen konstante, ganzzahlige Werte sein. Deshalb funktionieren die folgenden Initialisierungen nicht. Falsch: int i = 5; int testArray[i]; // i ist keine Konstante const double d = 12; double doubleArray[d]; // Keine Fliesskommawerte als Array-Index Stattdessen stellen die folgenden Zeilen eine korrekte Initialisierung dar: Richtig: int intArray[100]; const int groesse = 12; double doubleArray[groesse]; // Verwendung einer const-Variable char charArray[groesse*2]; // Ergebnis ist ein konstanter Wert 5 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Arrays initialisieren Werte lassen sich an Arrays auf unterschiedliche Arten zuweisen. Die gängigste Methode ist, dahinter in geschweiften Klammern eine mit Komma getrennte Liste von Werten anzugeben. int lottotip[6] = {3, 5, 12, 23, 30, 42} // ohne Gewähr Die nächste Möglichkeit ist, nicht alle Werte zu belegen. Dann wird der Default-Wert des jeweiligen Datentyps benutzt. Bei Zahlen ist dies die 0. int meineWerte[3] = {1, 2}; // letzter Wert wird mit 0 belegt int arraymitNullen[5] = {0}; // Initialisierung aller Werte mit 0 Eine weitere Möglichkeit besteht darin, das Array die Grösse anhand der zugewiesenen Werte ermitteln zu lassen. Dazu lässt man den Wert in eckigen Klammern einfach weg. double messwerte[] = {12.3, 17.8, 5.5}; // ein Array der Groesse 3 6 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Array-Werte auslesen über den Index Um auf die Werte des Arrays zuzugreifen, muss eine genaue Angabe dazu gemacht werden, welches das gewünschte Element sein soll. Dies passiert über die Angabe des Index, wobei die Indizierung bei 0 und nicht bei 1 beginnt. Ein Array der Grösse N lässt sich bis zu einem maximalen Index von N-1 ansprechen. float notenwerte[] = {1, 1.3, 1.7, 2, 2.3, 2.7, 3, 3.3, 3.7, 4, 5}; cout << "Beste Note: " << notenwerte[0]; // erstes Element ausgeben int punkte[5] = {23, 8, 15, 20, 17}; cout << punkte[3]; // gibt 20 aus Um alle Werte auszugeben, eignet sich eine for-Schleife mit Verwendung des Index als Abbruchbedingung: 1 2 3 4 const int GROESSE =6; int lottotip [ GROESSE ] = {3 , 5 , 12 , 23 , 30 , 42} // ohne Gewaehr for ( int i = 0; i < GROESSE ; i ++) cout << i + 1 << " . Tippzahl : " << lottotip [ i ] << endl ; 7 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Einzelne Werte des Arrays ändern Einzelne Werte lassen sich über die Angabe des Index ändern. 1 2 3 4 5 6 7 8 9 const int GROESSE = 5; int messwerte [ GROESSE ] = {3 , 2 , 1000 , 7 , 5}; for ( int i = 0; i < GROESSE ; i ++) cout << i + 1 << " . Messwert : " << messwerte [ i ] << " , " ; cout << endl ; messwerte [2] = 8; for ( int i = 0; i < GROESSE ; i ++) cout << i + 1 << " . Messwert : " << messwerte [ i ] << " , " ; Andere Arraywerte lassen sich ebenfalls verwenden: 1 2 int myarray [3] = {13 , 28 , 32}; myarray [1] = myarray [2] - myarray [0]; 8 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Mögliche Fehlerquelle bei unerlaubtem Zugriff bei Arrays findet keine Überprüfung daraufhin statt, ob auf einen erlaubten Bereich zugegriffen wird oder nicht. Der Programmierer muss selbst dafür sorgen, dass kein ungültiger Zugriff wie dieser entstehen: 1 2 3 4 5 6 7 8 9 # include < iostream > using namespace std ; int testArray [4] = {17 , 4 , 21 , 39}; int main () { cout << testArray [4] << endl ; return 0; } Ein anderer unerlaubter Zugriff ist der folgende: 1 2 int testArray2 [3] = {5 , 6 , 7}; cout << testArray2 [ -3]; 9 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Mehrdimensionale Arrays Überblick 1 Arrays Mehrdimensionale Arrays 2 Strukturen 10 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Mehrdimensionale Arrays Mehrdimensionale Arrays definieren Bisher haben wir Arrays nur eindimensional betrachtet. Beispielsweise für Messwerte, die ein Sensor 6x am Tag liefert. double sensor[6]; Wert 1 Wert 2 Wert 3 Wert 4 Wert 5 Wert 6 Wie speichert am aber Messwerte von 4 dieser Sensoren, welche z.B. an anderen Positionen angebracht sind oder zu anderen Zeiten messen? Lösung: Mehrdimensionale Arrays, die einer Matrix oder Tabelle gleichen. double sensoren[4][6]; Jede Zeile stellt die Messwerte eines eigenen Sensors dar. Sensor 1 Sensor 2 Sensor 3 Sensor 4 Wert 1 Wert 1 Wert 1 Wert 1 Wert 2 Wert 2 Wert 2 Wert 2 Wert 3 Wert 3 Wert 3 Wert 3 Wert 4 Wert 4 Wert 4 Wert 4 Wert 5 Wert 5 Wert 5 Wert 5 Wert 6 Wert 6 Wert 6 Wert 6 11 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Mehrdimensionale Arrays Mehrdimensionale Arrays -- Initialisierung und Zugriff bool platzreservierung_privatkino[5][3] = { false }; Reihe 1 Reihe 2 Reihe 3 Reihe 4 Reihe 5 false false false false false false false false false false false false false false false platzreservierung_privatkino[1][2] = true; // Reservierung erfolgt Reihe 1 Reihe 2 Reihe 3 Reihe 4 Reihe 5 false false false false false false false false false false false true false false false 12 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Arrays Mehrdimensionale Arrays Mehrdimensionale Arrays durchlaufen Genauso wie eindimensionale Arrays mit einer for-Schleife durchlaufen werden, so lassen sich mehrdimensionale Arrays mit zwei verschachtelten for-Schleifen ausgeben. 1 2 3 4 5 6 7 8 9 10 const int AUSSEN_MAX = 5; const int INNEN_MAX = 3; int myarray [ AUSSEN_MAX ][ INNEN_MAX ] = { false }; myarray [2][1] = true ; for ( int lauf_aussen =0; lauf_aussen < AUSSEN_MAX ; lauf_aussen ++) { for ( int lauf_innen =0; lauf_innen < INNEN_MAX ; lauf_innen ++) { cout << "[" << lauf_aussen << " ][ " << lauf_innen << " ]: " << myarray [ lauf_aussen ][ lauf_innen ] << " " ; } cout << endl ; // Zeilenumbruch } 13 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Überblick 1 Arrays Mehrdimensionale Arrays 2 Strukturen 14 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Motivation Bisher haben wir einzelne Datentypen eines Typs verwendet, z.B. int x. Durch Arrays können wir eine Menge des gleichen Datentyps gesammelt ablegen, z.B. int temperaturen[24]. Die einzige Einschränkung, die uns noch verbleibt ist diejenige, dass es sich dabei immer um den gleichen Datentyp handeln muss. Unterschiedliche Datentypen lassen sich nicht so ohne weiteres gemeinsam verwenden, da der Compiler vorher nicht wissen kann, welche Datentypen wie mit einander kombiniert werden sollen. Da dies vom Programmierer von einer Problemstellung zur anderen jedesmal neu festgelegt wird, würde es auch keinen Sinn ergeben, alle möglichen Kombinationen in der Programmiersprache vorzuhalten. Stattdessen ermöglicht es C++ dem Programmierer, eigene Datentypen flexibel zu definieren und Daten unterschiedlichen Typs strukturiert abzuspeichern. Daher kommt auch der Name struct (engl. für Struktur). 15 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Eigene Strukturen definieren Die allgemeine Form, um eine Struktur in C++-Programmen zu definieren, ist die folgende: struct strukturname { struct strukturname { memberdatentyp1 membername1; memberdatentyp1 membername1; memberdatentyp2 membername2; memberdatentyp2 membername2; ... ... memberdatentypN membernameN; memberdatentypN membernameN; } name(n); }; Eine Datenstruktur ist eine Gruppe von Datenelementen (members), die unter dem gleichen Objektnamen (strukturname) zusammengefasst sind. Ein konkretes Element dieser Zusammenfassung wird unter seinem Namen (objektname) angesprochen. Sobald eine Datenstruktur definiert ist, kann diese nicht mehr um weitere Member erweitert werden. Es lassen sich aber weitere Strukturelemente erzeugen. Dadurch wird klar, dass die Angabe name nicht gleich bei der Deklaration erfolgen muss, sondern optional ist. 16 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Erstes Beispiel zu struct Wir wollen einen Datentyp erstellen, um Produkte in unserem Programm abzuspeichern. Ein Produkt besitzt eine Bezeichnung (string) und einen Preis (float). 1 2 3 4 5 # include < string > struct produkt { string bezeichnung ; float preis ; }; // Achten Sie auf das abschliessende Semikolon ! Ein neues Produkt kann im Programm nach der Deklaration der Struktur so angelegt werden: produkt mobiltelefon ; Eine zweite Möglichkeit ist dies direkt bei der Deklaration zu tun: 1 2 3 4 5 # include < string > struct produkt { string bezeichnung ; float preis ; } mobiltelefon ; Mehrere solcher Objekte lassen sich durch Komma getrennt erzeugen. 17 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Zugriff auf Memberdatentypen von struct Um Zugriff auf die Memberdatentypen zu erhalten, wird der Punkt-Operator (.) verwendet. Er trennt dabei das konkret angelegte Objekt auf der linken Seite vom Memberdatentyp rechts davon. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # include < iostream > # include < string > using namespace std ; int main () { struct produkt { string bezeichnung ; float preis ; } handy ; produkt stuhl ; // ein weiteres Objekt anlegen handy . bezeichnung = " Klingeling 2000 " ; handy . preis = 100; stuhl . bezeichnung = " Bequemsitzer 900 " ; // Preis steht noch nicht fest cout << handy . bezeichnung << " kostet " << handy . preis << endl ; cout << stuhl . bezeichnung << " kostet " << stuhl . preis << endl ; return 0; } 18 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Einlesen von Struktur-Membervariablen Das Einlesen von Strukturvariablen geschieht ebenfalls durch den Punktoperatorzugriff. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include < iostream > # include < string > using namespace std ; int main () { struct Auto { string name ; int tueren ; } userauto ; cout << " Bitte Bezeichnung fuer das Auto eingeben : " ; cin >> userauto . name ; cout << " Bitte die Anzahl der Tueren eingeben : " ; cin >> userauto . tueren ; cout << " Name des Autos : " << userauto . name << endl ; cout << " Anzahl Tueren : " << userauto . tueren << endl ; return 0; } 19 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Strukturen verschachteln Strukturen können nicht nur primitive Datentypen wie char, int, float, usw. aufnehmen. Sie sind auch in der Lage, andere Strukturen als Membervariablen zu akzeptieren, wodurch verschachtelte Strukturen entstehen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # include < string > struct person { string vorname ; string nachname ; }; struct buch { person autor ; // verwendet struct person als Membervariable string titel ; }; buch thriller ; thriller . autor . vorname = " Susi " ; // Zugriff auf struct person thriller . autor . nachname = " Sonnenschein " ; thriller . titel = " Schockierende Programme in C ++ " ; Der Zugriff erfolgt also durch eine Aneinanderreihung von Punktoperatoren, bis zur gewünschten, darunterliegenden Struktur. 20 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Arrays von Strukturen anlegen Strukturen lassen sich auch als Arrays anlegen, so dass jedes Element des Feldes ein struct beinhaltet. 1 2 3 4 5 6 7 8 9 10 11 12 const int MAX = 11; // soviel Freunde sollt ihr sein ... struct spieler { int trikotnummer ; string name ; } mannschaft [ MAX ]; for ( int i =0; i < MAX ;i ++) { cout << " Name des Spielers Nr . " << i +1 << " : " ; cin >> spieler [i ]. name ; mannschaft [i ]. trikotnummer = i +1; } Beim Zugriff muss zuerst die Array-Indexposition angegeben werden und anschliessend kann wie gewohnt mit dem Punktoperator auf die Membervariablen zugegriffen werden. 21 / 22 Zusammengesetzte Datentypen --Arrays und Strukturen Strukturen Nachteile von structs Ein Nachteil von Strukturen ist, dass man separate Prüfungen durchführen muss, um diese mit korrektem Inhalt zu füllen. Die Struktur kümmert sich nicht von selbst darum, um diese Logik sich muss der Programmierer selbst kümmern. Ein weiterer Nachteil ist, dass Objekte zwar Eigenschaften in Form ihrer Membervariablen besitzen, aber mit diesen keine Aktionen direkt zu verknüpfen sind. So können beispielsweise Autos eine Farbe, eine Motordrehzahl und eine Menge an Rändern besitzen. Aber Aktionen wie fahren, tanken oder parken, die nur dieses Objekt durchführen kann, sind damit nicht möglich. Um diese Probleme zu lösen, wurden in C++ sogenannte Klassen und Objekte eingeführt. Diese können sowohl Eigenschaften besitzen, als auch Verhalten (Aktionen) durchführen. Da struct ein Sprachmittel ist, das von C übernommen wurde, stellen Klassen und Objekte von ihrer Funktionalität her mächtigere Sprachmittel dar. Trotzdem gibt es durchaus viele Anwendungsbereiche für structs. 22 / 22