Programmieren in C/C++ und MATLAB Sven Willert Sabine Schmidt Christian-Albrechts-Universität zu Kiel CAU 5-1
Übung Schreiben Sie ein Programm, das die Zahl π durch π = 4 4 4 4 4 4 + + +... 3 5 7 9 11 approximiert. Schreiben Sie das Programm einmal mit einer for-schleife und einmal mit einer do-while-schleife. Die Schleifen sollen jeweils abbrechen, wenn die gewünschte Genauigkeit (über Benutzereingabe vorgegeben) erreicht ist, und das Ergebnis soll auf den Bildschirm ausgegeben werden. Zusatzaufgabe: Schreiben Sie das Programm so um, dass es eine while-schleife verwendet. CAU 5-2
#include <cstdlib> #include <iostream> using namespace std; /* Berechnung von Pi = 4-4/3 + 4/5-4/7 + 4/9... */ Übung: Berechnung von π mit for-schleife int main(int argc, char *argv[]) { // Variablen double pi=0.0, pi_alt=0.0, vorzeichen=1.0, fehler; cout << " Berchnung von pi ueber die Reihe pi = 4-4/3 + 4/5-4/7 + 4/9... " << endl; cout << " Bitte gewuenschte Genauigkeit in Prozent eingeben: "; cin >> fehler; fehler /= 100.0; for (int i = 1; i < 10000000; i= i+2){ pi = pi + vorzeichen*4.0/((double)i); cout << " i: " << i << ", vorzeichen: " << vorzeichen << ", pi: " << pi << " (pi - pi_alt)/pi " << (pi - pi_alt)/pi << endl; } if(((pi-pi_alt)/pi < fehler) && ((pi-pi_alt)/pi > -1.0*fehler)) break; pi_alt = pi; vorzeichen *= -1.0; /* Ausgabe */ cout << " pi = " << pi << endl; } system("pause"); return EXIT_SUCCESS; CAU 5-3 (π = 3.141592653589... )
#include <cstdlib> #include <iostream> using namespace std; /* Berechnung von Pi = 4-4/3 + 4/5-4/7 + 4/9... */ CAU 5-4 Übung: Berechnung von π int main(int argc, char *argv[]) { // Variablen double pi, pi_alt, vorzeichen, fehler, teiler; /* Eingaben */ cout << " Berechnung von pi ueber die Reihe pi = 4-4/3 + 4/5-4/7 + 4/9... " << endl; cout << " Bitte gewuenschte Genauigkeit in Prozent eingeben: "; cin >> fehler; fehler /= 100.0; /* while Schleife */ teiler=1.0; pi_alt = 0.0; vorzeichen = 1.0; pi = 0.0; do { pi_alt = pi; pi = pi + vorzeichen*4.0/teiler; cout << "Teiler: " << teiler << " pi: " << pi << endl; vorzeichen *= -1.0; teiler = teiler + 2.0; } while(((pi-pi_alt)/pi > fehler) ((pi-pi_alt)/pi < -1.0*fehler)); /* Ausgabe */ cout << " pi = " << pi << endl; system("pause"); return EXIT_SUCCESS; } mit do-while-schleife
Hinweise: Übung: Berechnung von π 4/7 ist eine int-division und ergibt 0 4.0/7.0 ist eine double-divison und ergibt 0.5714... Bei der for()-schleife kann der Zählindex fast beliebig geändert werden: for(int i=0; i< 1000; i++){... } // i++ ist dasselbe wie i=i+1 for(int i=0; i< 1000; i=i+2){... } for(int i=0; i< 1000; i=i*4){... } Es können auch double Zahlen als Zählindex verwendet werden, dies wird allerdings nicht empfohlen. Lieber int als Zähler und dann auf double casten: for(int i=0; i< 1000; i=i+1){ zahl = 4.0*(double)i; } Der Compiler gcc erlaubt es, in die Abfrage der while-schleife noch weitere Anweisungen hineinzuschreiben. Unbedingt unterlassen! CAU 5-5
Felder Bisher hatten wir nur eindimensionale Variablen betrachtet. Von diesen hatten wir immer nur eine, und hatten wir zwei, hatten diese unterschiedliche Namen. Diese Art des Datenhandlings funktioniert nicht mehr bei großen Datenmengen oder strukturierten Datenmengen (Matrix, Liste). Daher kann man in C/C++ Felder verwenden. Felder nennt man auf englisch array, und so heißen diese auch in C/C++. Ein array ist eine durchnummerierte Menge von Variablen desselben Typs. Sie können von jedem Datentyp Felder bilden. Zur Deklaration muss dazu außer dem Typ und dem Namen noch die Anzahl der Elemente in eckigen Klammern hinter dem Namen angegeben werden: Deklaration eines int Feldes mit 10 Elementen: int a[10]; CAU 5-6
Felder Aufgrund dieser Anweisung reserviert der Speicher nicht nur für eine int-variable Speicher, sondern für 10 int-variablen. Die Abbildung rechts zeigt den Arbeitsspeicher (M) sowie dessen einzelne Speicherplätze (durch Kästchen). Das Feld a belegt nun hintereinander im Arbeitsspeicher wie in einer Tabelle Speicherplätze (Größe entspricht dem Datentyp). Der Zugriff auf das Feld, sowohl zum Beschreiben als auch zum Lesen von Feldelementen erfolgt durch den Indexoperator [], d.h. die eckigen Klammern. Innerhalb dieser Klammern gibt man nach dem Feldnamen den Index des Feldelementes an, auf das man zugreifen möchte: a[0] = 3; a[1] = 5; CAU 5-7
Felder Die Indizierung eines Feldes mit n Elementen läuft immer (!) von 0 bis n-1!! Beispiel: int a[5]; a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; a[4] = 5; // a[5] ist kein Element des Feldes a!!! Zugriffe außerhalb führen typischerweise zum Programmabsturz, weder Compiler noch Betriebsystem verhindern diese Zugriffe. Dabei werden Speicherinhalte außerhalb des Feldes geändert, was oft im späteren Verlauf des Programms zum Absturz führt (Typische Fehlermeldung: Segmentation fault. CAU 5-8
Felder Initialisierung eines Feldes: Auch Felder müssen initialisiert werden. Bei kleinen Feldern kann man das direkt bei der Deklaration machen: int a[3] = {1, 2, 3}; Bei großen Feldern ist das unübersichtlich und unökonomisch. Daher werden hier oft for-schleifen eingesetzt: int a[100]; for(int i=0; i<100; i++) a[i] = 0; Achtung: Bei dieser Bedingung immer bei 0 beginnen und echt kleiner ( < ) als Obergrenze angeben. CAU 5-9
Felder Mehrdimensionale Felder: Felder können auch andere Dimensionen haben. Dazu werden weitere Größenangaben angehängt. int a[100][100]; Dadurch wird eine 100*100 Matrix vom Typ int deklariert. Initialisierung analog 1D- Feldern durch verschachtelte for-schleifen: int a[100][100]; for(int i=0; i<100; i++){ for(int j=0; j<100; j++) a[i][j] = 0; } Zeilen i Spalten j CAU 5-10
Felder Einschränkungen von Feldern: Für Felder muss die Anzahl der Elemente direkt im Code festgelegt werden. Der Wert (der Anzahl) muss dabei eine Konstante sein. Folgender Code ist daher falsch und ergibt einen Compiler-Fehler: int n=5; int a[n][n]; D.h. die Größe des Feldes ist fest einprogrammiert, sodass in den meisten Fällen entweder Speicherplatz verschenkt wird oder das Feld zu klein ist. Das kann man in C anhand von dynamischer Speicherverwaltung umgehen (kompliziert), in C++ gibt es dafür die sehr praktische Datenstruktur vector CAU 5-11
Vector In der STL C++ Standardbibliothek (Standard Template Library) sind die so genannten Containerklassen von C++ hinterlegt. Diese stellen eine ganze Reihe von nützlichen Datenstrukturen bereit (Listen, Vektoren, Warteschlange, Stack,...) Durch einbinden (#include std) werden diese zugänglich gemacht. Dafür muss zusätzlich die folgenden Zeile am Ende der #include- Anweisungen stehen: #include <vector> using namespace std; (wie für cout, cin,...) Der wichtigste Container ist der sogenannte vector. Dieser wird dann verwendet, wenn die Anzahl der Elemente eines eindimensionalen Feldes bei der Programmierung unbekannt ist. Die Datenstruktur vector erlaubt willkürlichen Zugriff auf einzelne Elemente über deren Indexwert. Man kann von jeder Datenstruktur und jedem Datentyp einen vector bilden, auch verschachtelt. CAU 5-12
Vector Deklaration eines Vektoren: vector <Typ> Name; // Macht einen leeren Vector vector <Typ> Name(n); // Macht einen Vector mit n Elementen vector <Typ> Name(n, elem); // Macht einen Vector mit n Elementen // die alle Kopien von elem sind Name.~vector<Typ>(); // Löscht alle Elemente und gibt den //Speicher frei Beispiel: vector <int> v(n,0); Erzeugt einen Vector, der int-werte enthält und Anzahl n Elemente besitzt, die alle gleich 0 sind. So kann also der Vektor gleich initialisiert werden. CAU 5-13
Vector Wenn noch nicht klar ist, wie lange der Vektor wird, können zunächst die einzelnen Elemente erzeugt werden und dann an den Vektor angehängt werden. vector <Typ> tvec; vector <int> ivec; Typ Instanz; int zahl; //Wertzuweisung an Instanz zahl = 5; tvec.push_back(instanz); ivec.push_back(zahl); Da so nicht klar ist, wie lange der Vektor ist, kann man das den Vektor fragen: int laenge = tvec.size(); for(int i=0; i<laenge; i++) {//Tue irgendetwas} tvec.clear(); //Löscht alle Elemente von tvec und auch schauen ob er überhaupt ein Element enthält: tvec.empty(); CAU 5-14
Vector tvec.push_back(elem) hängt ein Element an den Vektor an. Wenn das Element irgendwo eingefügt werden soll, muss ein Iterator verwendet werden. Dies ist ein spezieller Datentyp in C++, der die Indizierung der Containertypen übernimmt. Dieser Iterator muss eigenständig deklariert und initialisiert werden: std::vector<typ>::iterator pos; // Deklaration Iterator pos pos = tvec.begin(); // Initialisierung Iterator pos Im Vektor kann man dann normale Indizierung verwenden: pos = tvec.begin() + n; // (n) tes Element Typ var; // Erzeuge Variable vom Typ Typ tvec.insert(pos,var); // Einfügen von var an pos in tvec CAU 5-15
Übung Vektoren Schreiben Sie ein Programm, dass einen Vektor über die Tastatureingabe mit zehn Werten belegt. Anschließend soll die Möglichkeit bestehen, n weitere Werte (Tastatureingabe) einzufügen, und zwar an Positionen und mit Werten, die ebenfalls über die Tastatur eingegeben werden können. Am Ende soll der komplette Vektor ausgegeben werden können. Hinweis: Verwenden Sie den Container vector und Iteratoren. CAU 5-16
Übung Programmstruktur: - Variablendeklaration Welche Variablen? zumindest den int-vector und den Iterator - Variableninitialisierung Welche Initialisierungen sind notwendig? - Einlesen des Vektorinhalts Eingabe über die Tastatur von10 Werten ( cin >>... ), jede Zahl mit Enter bestätigen. Hier bietet sich ein for() Schleife an. - Eingabe der Anzahl der neu einzufügenden Vektorelemente Eingabe über die Tastatur (ein Wert) - Eingabe der Position und des Wertes für jedes einzufügende Element Auch hier bietet sich wieder eine for() Schleife an, in der die Eingabe von Position und Wert für jedes zusätzliche Element gelesen und auch gleich eingefügt wird. Blöcke der for()-schleife mit {Anweisungen;} - Routine zur Ausgabe des neuen Vektors auf den Bildschirm for() Schleife zur Ausgabe der Einzelnen Elemente des Vectors; vorher Abfrage der Länge des Vectors CAU 5-17