Informatik Heinrich Krämer Hochschule für Technik, Wirtschaft und Kultur Leipzig (FH) Fachbereich Informatik, Mathematik und Naturwissenschaften Folie 2 1 Einführung Einsatz von Rechnern im Automobilbau – Entwurf • Festigkeitsanalysen • Simulierte Crashtest – Produktion • Personalverwaltung • Robotik • Ein-, Verkauf, Lagerhaltung – Betrieb • Motorsteuerung (Einspritzanlage) • Traktionskontrolle, ABS • Navigationssystem Wesentliche Merkmale lassen sich nicht oder nur mit immensem Aufwand ohne Rechnereinsatz realisieren. In anderen Brachen ergibt sich ein ähnliches Bild. Grundlagen der Informatik Folie 3 1.1 Algorithmus Ein Algorithmus ist die Beschreibung eines Verfahrens, wie aus gegeben Eingabedaten die gewünschten Ausgabedaten berechnet werden können. Ausgangspunkt bei der Entwicklung eines Algorithmus ist eine exakte Spezifikation Diese besteht aus – Eingabe Genaue Angabe der zulässigen Eingabewerte – Ausgabe Angabe der Wertebereiche des Ausgabedaten. Mögliche Fehlermeldung – Funktionaler Zusammenhang Grundlagen der Informatik Folie 4 1.1 Algorithmus Entwicklung eines Algorithmus nach der Top-Down-Methode Problemanalyse Welche mögliche Eingabedaten können auftreten? Welche Eingabedaten sollen unzulässig sein? Welche Ausgabedaten sollen berechnet werden? Wie soll auf unzulässige Eingaben reagiert werden? Welcher funktionale Zusammenhang besteht zwischen diesen? Spezifikation Zergliedern in Teilprobleme Häufig: Eingabe, Verarbeitung, Ausgabe Welche Teilergebnisse müssen berechnet werden? Bestimmung der Reihenfolge abstrakter Algorithmus Codierung Formulierung des Algorithmus in einer Programmiersprache Grundlagen der Informatik Folie 5 1.1 Algorithmus Beispiel Berechnung GGT (Größter gemeinsamer Teiler) Eingabe: Zwei natürliche Zahlen a, b Ausgabe: natürliche Zahl g Funktionaler Zusammenhang: a und b sind durch g ohne Rest teilbar g ist größte Zahl mit dieser Eigenschaft Algorithmus 1. Eingabe(a); 2. Eingabe(b); 3. solange a b 3.3 falls a < b : b := b – a 3.3 sonst a := a – b 4 Gebe a aus Grundlagen der Informatik Adressbus Datenbus Steuerbus Central Processing Unit Leitwerk Zustand Operandenholen Befehlholen Dekodieren Ausführen Programmzähler Nr 0 1 2 3 4 5 6 7 Register Inhalt 15 Adresse 0 1 2 3 4 5 6 7 8 9 10 Inhalt 100 101 102 103 104 105 106 107 108 109 110 111 112 113 Lade R0 <- [5] Lade R1 <- [6] Falls R0 R1 gehe 110 Falls R0 > R1 gehe 107 R2 <- R1 - R0 Speichere [5] <- R2 gehe 100 R2 <- R0 - R1 Speichere [6] <- R2 gehe 100 Ende Peripherie 15 10 100 ALU Arithmetic Logic Unit Folie 7 1.2 Programmablaufpläne / Flußdiagramme Startknoten Start Zusammenführung Endeknoten Ende Anweisung true ? false Grundlagen der Informatik Anweisungsknoten Verzeigungsknoten Ein- / Ausgabeknoten Unterprogramm Unterprogrammaufruf Folie 8 Beispiel: GGT (größter gemeinsamer Teiler) Start a, b false true a b? true b = b -a Ende Grundlagen der Informatik a < b? false a=a-b C-Notation int main() { int a,b; scanf(`` %d %d´´,&a,&b) while (a != b) { if (a < b) { b = b - a; } else { a = a - b; } } } Folie 9 2 Darstellung von Information Programme und Daten werden im Speicher als Bytes (Achtergruppen von 0/1 den Bits) gespeichert. Die Bedeutung der Information ist hierbei von der Interpretation abhängig Mögliche Interpretationen bei einem Intel-Proz. Adresse +0 +1 +2 +3 Inhalt 1111 0111 1100 0010 0100 0001 0110 0010 Befehl: test dx, 0110001001000001b Ganzzahl 1648476919 Gleitkommazahl 1,513762354851 269 Zeichen TAb Die Interpretation der Daten wird bei einer Hochsprache durch den Datentyp festgelegt Grundlagen der Informatik Folie 10 2.1 Skalaren Datentypen in C/C++ Der Datentyp int int dient zur Modellierung von ganzzahligen Werten. Es ist in C üblich, diesen Datentyp auf die Maschinenwortbreite abzubilden. Beispiele für Maschinenwortbreiten Zahlenbereich i286 16-Bit -215...215-1 -32768... 32767 i386 32-Bit -231...231-1 -2147483648... 2147483647 Pentium 32-Bit -231...231-1 -2147483648... 2147483647 Sparc 64-Bit -263...263-1 -9,223372036855e+18... 9,223372036854e+18 Modifizierer signed Es werden positive und negative Werte betrachtet (Standard) unsigned Es werden nur positive Werte betrachtet long Es gilt immer, dass jeder int Wert in long darstellbar ist short Es gilt, dass jeder short Wert in int darstellbar ist Grundlagen der Informatik Folie 11 2.2 Skalaren Datentypen in C/C++ Der Datentyp float (Gleitkommazahlen) Float dient zur Modellierung von reellen Zahlen. Hierbei ist jedoch zu beachten, dass diese nur mit endlicher Genauigkeit dargestellt werden können. Die Darstellung ist in der IEEE-Norm 754 festgelegt. Die rechnerinterne Darstellung erfolgt analog dem „wissenschaftlichen Format“ bei einem Taschenrechner als Vorzeichen Mantisse 2Exponent Hierbei haben Mantisse und Exponent eine feste Länge, welche die Genauigkeit der Darstellung bestimmt float Einfache Genauigkeit double Doppelte Genauigkeit Konstante bei Gleitkommazahlen 1.0 (Gleitkomma Eins) 0.31417e+1 (Gleitkomma 3,1417) Grundlagen der Informatik Folie 12 2.2 Skalaren Datentypen in C/C++ Der Datentyp char Dient der Darstellung von Zeichen. Eigentlich handelt es sich hierbei um ein 8-Bit Ganzzahlformat. Daher gibt es auch für char die Modifizierer signed (Standard!) und unsigned. Die Zeichen werden üblicherweise im ASCIIFormat (American Standard Code for Information Interchange) dargestellt. Eine Zeichenkonstante wird in Einfachhochkommas eingeschlossen. 'c' Das Zeichen c Zusätzlich gibt es noch einige spezielle Zeichen \t Tabulator \n neue Zeile (newline) \“ Doppelhochkomma \‘ Einfachhochkomma \\ back slash \<Oktalcode> zur Notation von nicht druckbare Zeichen Grundlagen der Informatik Folie 13 2.2 Skalaren Datentypen in C/C++ Der Datentyp bool dient der Modellierung von Wahrheitswerten. Es gibt nur die beiden Konstanten: false falsch true wahr Es gilt false < true. In C/C++ wird jeder Wert ungleich 0 als true und jeder Wert gleich 0 als false angesehen. Besonderheiten 0 false 17 true 0.0 false -0.0 true Grundlagen der Informatik Folie 14 Notation Backus-Nauer-Form (BNF) Produktion <linke Seite> ::= { <Nichtterminale> | <Terminale> } Methazeichen [ ] optionales Vorkommen { } beliebig oft ... | ... Auswahl Beispiel <Bezeichner>: jedes Objekt muss einen eindeutigen Namen erhalten <Bezeichner> ::= <Buchstabe> { <Buchstabe> | <Ziffer> } <Buchstabe> ::= a|b|...|z|A|B|...|Z|_|$ (Großkleinschreibung ist relevant) <Ziffer> ::= 0|1|...|9 Legale Bezeichner x11, Beta, beta Ein_Sehr_Langer_Bezeichner __system, $syscall Grundlagen der Informatik Illegale Bezeichner 1_Fehler, faulty-name Folie 15 2.3 Ausdrücke Typumwandlung (typ) Ausdruck Inkrement/Dekrement ++ Inkrement (+1) -- Dekrement (-1) Unitäre Operatoren + Positiv - Negativ ! Logisches Nicht ~ Bitweises Komplement Multiplikationsoperatoren * Multiplikation / Division % Modulo Additionsoperator + Addition - Subtraktion Grundlagen der Informatik Schiebeoperatoren << Schieben nach links >> Schieben nach rechts Vergleichsoperatoren > Größer < Kleiner >= Größergleich <= Kleinergleich Gleichheitsoperatoren == Gleichheit != Ungleichheit Bitoperatoren & Bitweises UND ^ Bitweise Antivalenz | Bitweises ODER Logische Operatoren && UND || ODER Folie 16 2.3 Ausdrücke Bedingte Ausdrücke <Ausdruck1> ? <Ausdruck2> : <Ausdruck3> Der Ausdruck1 wird bewertet ergibt dieser wahr, so ist der Wert des Ausdrucks durch Ausdruck2 gegeben sonst durch Ausdruck3 Zuweisungsausdrücke <lwert> = <Ausdruck> Ein <lwert> ist ein Datenobjekt das modifiziert werden darf. Die Auswertung erfolgt von rechts nach links. Verkürzte Zuweisungen *= /= Eine verkürzte Zuweisung der Form %= += <lwert> <op>= <Ausdruck> -= >>= hat die Bedeutung <<= <lwert> = <lwert> <op> <Ausdruck> &= ^= |= Grundlagen der Informatik Folie 17 Programmentwicklung #include <Standarbiliothek.h> #include "Header.h" int main () { <Anweisungen> return 0 } C-Prog. <name>.cpp Präprozessor Compiler Binder <name>.exe Grundlagen der Informatik Folie 18 C-Anweisungen Ausdrucksanweisung <Ausdruck>; Ein Ausdruck gefolgt von einem Semikolon Der Ausdruck kann leer sein Blockanweisung { <Anweisung>... } Ein Liste von Anweisungen in geschweiften Klammern Variablendeklaration <typ> <Varname> [ = <Ausdruck>]{ , <Varname> [ = <Ausdruck>]}; Jede Variable muß vor ihrer ersten Verwendung deklariert sein. Die Deklaration beginnt mit der Deklaration und endet mit dem Block, in dem sie deklariert wurde. Grundlagen der Informatik Folie 19 if-Anweisung <if-Anweisung> ::=if (<Ausdruck>) <Anweisung(1)> [else <Anweisung(2)> ] Bem.: Falls kein else-Teil vorhanden ist und der Ausdruck sich zu falsch auswertet, so wird keine Aktion durchgeführt. Beispiele: Ausdruck Anweisung(1) Grundlagen der Informatik Anweisung(2) if (x < 0) x = -x * a; else x = x * a; if (x < 0) { d = 1/x; a = a + d; } Folie 20 switch-Anweisung <switch-Anweisung> ::= switch (<Ausdruck>) { case <Konstante> : { <Anweisung> } { case <Konstante> : { <Anweisung> }} [ default : <Anweisung> { <Anweisung> }] } Zunächst wird der Ausdruck bewertet. Anschließend wird zu dem Fall verzweigt, dessen Konstante gleich dem Wert des Ausdrucks ist. Anschließend werden alle nachfolgenden Anweisungen ausgeführt. Die Ausführung kann durch die Anweisung break; unterbrochen werden. Ist keine Konstante gleich dem Wert des Ausdrucks wird zu default verzweigt. Falls dieser nicht vorhanden ist, wird keine Anweisung ausgeführt. Grundlagen der Informatik Folie 21 k1 switch-Anweisung Beispiel: Menü-Auswahl char c; printf_s("Bitte Aktion auswählen:"); printf_s("\t(1)\tSpeichern\n"); printf_s("\t(2)\tBeenden\n"); scanf_s("%c",&c); switch ( c ) { case '1' : store(); break; case '2' : exitprog(); break; default: printf_s("Fehler: kein gültiger Menü-Punkt\"); } Grundlagen der Informatik Folie 21 k1 kraemer; 09.02.2016 Folie 22 while/do-Schleife <while-Schleife> ::= while (<Ausdruck>) <Anweisung> <do-Schleife> ::= do { <Anweisung> } while (<Ausdruck>); false Ausdruck Anweisung true Anweisung Ausdruck true false Grundlagen der Informatik Folie 23 while/do-Schleife Grundlagen der Informatik Folie 24 for-Schleife <for-Schleife> ::= for ( <Init-Anweisung> <Ausdruck1> ; <Ausdruck2> ) <Anweisung> hat die Bedeutung <Init-Anweisung> while (<Ausdruck1>) { <Anweisung> <Ausdruck2>; } Alle Schleifen können folgende Anweisungen enthalten break; die Schleife wird abgebrochen continue; die Iteration wird am Beginn der Schleife fortgesetzt Grundlagen der Informatik Folie 25 for-Schleife Grundlagen der Informatik Folie 26 Benutzerdefinierte Datentypen Der Felddatentyp und Zeichenketten Der Felddatentyp dient der Zusammenfassung gleicher Elemente Die Deklaration einer Feldvariablen erfolgt durch <typ> <Varname>[<ganzzahlige Konstante>][[<ganzzahlige Konstante>]]; Die Anzahl der eckigen Klammern ist die Dimension des Feldes (Array) Felder mit einer variablen Anzahl von Elementen sind in ANSI-C nicht zulässig. Grundlagen der Informatik Folie 27 Zeichenketten (strings) Zeichenketten sind eindimensionale Felder des Typs char Hierbei wird das Ende der Zeichenkette durch das Zeichen mit dem Wert 0 dargestellt ('\0'). Die Zeichenkette muß also um eins größer sein, als die Anzahl der Buchstaben. Beispiel char Hello[13]; Hello = "Hello World\n"; Darstellung im Speicher Hello H e l l o W o r l d \n \0 0 1 2 3 4 5 6 7 8 9 10 11 12 Grundlagen der Informatik Folie 28 Allgemeine Felder Felder lassen sich zu jedem Datentyp deklarieren Beispiel float Vector[10]; float Matrix[3][3]; Gleitkommawerten. Ein Feld mit zehn Gleitkommawerten (0..9) Ein zweidimensionales Feld mit 3 3 Darstellung im Speicher Matrix [0][0] [0][1] [0][2] [1][0] [1][1] [1][2] [2][0] [2][1] [2][2] Grundlagen der Informatik Folie 29 Allgemeine Felder Zugriff auf Feldelemente <Fname>[<ganzzahliger Ausdruck>]{[<ganzzahliger Ausdruck>]} Hierbei dürfen höchstens so viele Indices angegeben werden, wie das Feld Dimensionen hat (weniger sind zulässig) Bsp. float x, vector[10], matrix[10][10]; int i; x = vector[5]; x = matrix[3][4]; vector = matrix[2] matrix[i+7][i-2] = x; In C/C++ ist der Programmier dafür verantwortlich, dass der Index-Wert im zulässigen Bereich bleibt. Ein zu großer oder kleiner Wert führt zu einer sog. Feldgrenzenüberschreitung. Das Ergebnis ist undefiniert. Grundlagen der Informatik Folie 30 Verbunddatentypen (struct, class) Der Verbunddatentyp dient der Zusammenfassung verschiedener Datentypen Deklaration eines Verbunddatentyps struct <SBezeichner> { { <Element-Deklaration>; } } [ <Verbund-Variable>]; <Element-Deklaration> ::= <typ> <element> { , <element> } | <Element-Funktion> Zugriffsteuerung public: Die Elemente sind überall sichtbar und zugreifbar private: Die Elemente sind nur in Elementfunktionen zugreifbar protected: Die Elemente sind abgeleiteten Klassen sichtbar Grundlagen der Informatik Folie 31 Verbunddatentyp Deklaration eines Verbunddatentyps class <CBezeichner> { { <Element-Deklaration>; } } [ <Verbund-Variable>]; Unterschiede struct Adresse { // standard public: char NachName[20]; char VorName[20]; int PLZ; char Ort[20]; char Strasse[30]; }; Grundlagen der Informatik class Adresse { public: // standard private: char NachName[20]; char VorName[20]; int PLZ; char Ort[20]; char Strasse[30]; }; Folie 32 Verbunddatentyp Der Zugriff auf ein Element eines Verbunddatentyps erfolgt durch den Selektor-Operator . Bsp. Einzelne Adresse Adresse adr; // Deklaration adr.NachName = "Krämer"; adr.VorName = "Heinrich"; Bsp. Adreßliste mit 10 Elementen Adresse al[10]; al[2].VorName = "Fritz"; al[2].Nachname = "Kaiser"; Grundlagen der Informatik Folie 33 Typdeklaration Zwischen einer Struktur-(struct) bzw. Klassendeklaration(class) und einem Feld besteht ein grundlegender Unterschied: float vector[10]; struct verbund{float x1, float x2}; // Hierdurch wird eine Variable // mit 10 Elementen angelegt // hier wird nur ein Datentyp deklariert // Eine Variable (Speicherbereich) muss // gesondert deklariert werden durch verbund v; Manchmal kann es sinnvoll sein, auch Felder als wiederverwendbaren Datentyp zu deklarieren hierzu dient das typedef <typdeklaration>; Bsp typedef float matrix[10][10]; // Deklaration des Datentyps matrix matrix A; // Variablendeklaration Grundlagen der Informatik Folie 34 Zeigertypen Durch Zeiger kann dynamisch Speicher angelegt werden Deklaration einer Zeigervariablen <typ> *<Varname>; Durch die Operatoren new wird neuer Speicher angelegt delete wird angelegter Speicher gelöscht Beachte: Dynamisch angelegter Bsp.: Anlegen eines Strings char *str; str = new char[256]; Speicher bleibt erhalten Löschen des Speichers delete [] str; // Beachte: Wird im Speicher ein Feld // angelegt so ist mit [] zu löschen Anlegen eines dynamischen Feldes float *f; f = new float[n] // n kann eine Variable sein Grundlagen der Informatik Folie 35 Zeigertypen In C/ C++ werden eindimensionale Felder als Zeiger interpretiert Der Inkrement / Dekrement-Operator dient zum Weiterschalten um ein Element Folgenden Programme sind äqivalent char ergstr[256], qstr[] = "Das ist eine StringInitialisierung"; for (int i = 0; ergstr[i] = qstr[i]; i++); // Alternativ for (char *p = ergstr, *q = qstr; *p++ = *q++;); // Das folgende Programm ist FALSCH for (;*ergstr++ = *qstr++;); // Hierbei werden die Zeiger auf die Strings verändert Grundlagen der Informatik Folie 36 Sichtbarkeit von Deklarationen • Jeder Bezeichner darf erst dann verwendet werden, wenn er vorher deklariert wurde. • In einer Umgebung darf ein Bezeichner nur einmal deklariert werden. (Ausnahmen später) • Eine Block-Anweisung (öffnende und schließende geschweifte Klammer) definiert eine neue Umgebung Sichtbarkeitsregeln • Alle Deklarationen vor dem Hauptprogramm sind überall sichtbar. (globale Deklarationen) • Deklarationen sind nur in der Umgebung und den jeweiligen Unterumgebungen sichtbar (lokale Deklarationen) • Eine Neudeklaration des gleichen Bezeichners in einer untergeordneten Umgebung ist zulässig. Diese überschreibt die Deklaration der äußeren Umgebung. Damit wird die äußere Deklaration unsichtbar Grundlagen der Informatik Folie 37 Sichtbarkeit von Deklarationen const int n; typedef float[n] feld; int main(…..) { feld f; int feld[5]; float z, x = 0.0; { float y, x = 1.0; y = 5.0 * x; } z = x + 10.0; } Grundlagen der Informatik Folie 38 Sichtbarkeit von Deklarationen Konventionen • Variablen sollten so lokal wie möglich deklariert werden • Die Verwendung von globalen Variablen muss vermieden weden Besonderheiten int main(….) { for (int i = 0; i < n; i++) { // In C erfolgt die Deklaration in der umschließenden // Umgebung in C++ innerhalb der for-Schleife } for (int i = 0; i < n; i++) { // illegal in C // notwendig in C++ } } Grundlagen der Informatik Folie 39 Unterprogramme Unterprogramme dienen – zur Zusammenfassung von häufig benutzten Anweisungsfolgen – zur Strukturierung von Programmen Faustregel: Eine fortlaufende Programmsequenz sollte nicht länger als ein bis zwei Bildschirmseiten lang sein (kein Spaghetticode) . Grundlagen der Informatik Folie 40 Unterprogramme Definition von Funktionen Eine Funktionsdefinition besteht aus einem Funktionskopf und einem Funktionsrumpf <Rückgabetyp> <FName>(<Parameterliste>) //Kopf { //Rumpf <Anweisungen> } Funktionen dürfen nur global d.h. vor der Funktion main definiert werden. Die Parameterliste besteht aus beliebig vielen (auch keinem) Parametern. Diese hat den Aufbau: [<Datentyp> <Pname> { , <Datentyp> <Pname>} ] Die Parameter bei der Definition werden als formale Parameter bezeichnet. Grundlagen der Informatik Folie 41 Unterprogramme Bsp. float Average(int x1, int x2, int x3) { float mean; mean = (x1 + x2 + x3) / 3.0; return mean; } Der Aufruf einer Funktion erfolgt durch die Angabe des Funktionsnamens und gefolgt durch die aktuellen Parameter. Hierbei muß beim Aufruf für jeden formalen Parameter ein entsprechender aktueller Parameter mit einem entsprechenden Typ eingesetzt werden. Bsp.: int a = 1, b = 2; float x = Average(a,3,b); Grundlagen der Informatik Folie 42 Unterprogramme Teilweise ist es unnötig, daß eine Funktion einen Wert zurückliefert. (Prozedur) Hierfür dient der "Datentyp" void, dieser bezeichnet keinen Rückgabewert Die Anweisung return dient zum Verlassen der Funktion. Diese gibt es in zwei Versionen: return <Ausdruck>; Diese Form ist bei Funktionen mit Rückgabewert anzugeben. Der Ausdruck muß sich zu einem entsprechenden Datentyp auswerten. Hier muß bei jedem Ausführungspfad mindestens eine return-Anweisung erreicht werden. return; Diese Form wird bei Funktionen ohne Rückgabewert (Prozeduren) benutzt. Hier kann die return-Anweisung entfallen. Es wird automatisch am Ende des Rumpfes zurückgesprungen Grundlagen der Informatik Folie 43 Parameterersetzungen Wertparameter Diese Parameter behalten den Wert nach dem Aufruf der Funktion bei, den sie vor dem Aufruf hatten. Änderungen innerhalb der Funktion haben nach außen also keine Wirkung. Standardmäßig sind alle Parameter in C Wertparameter. Referenzparameter Bei diesen Parametern wird nur ein Zeiger auf die entsprechende Speicherstelle übergeben. Änderungen innerhalb der Funktion wirken sich auf die Parameter aus. In C++ können Referenzparameter durch den Referenzoperator & deklariert werden. Bsp.: float RefParBsp(int &a) { a = 10 * a; return a/5.0; } Grundlagen der Informatik Folie 44 Sichtbarkeit und Parameterersetzungen int fctA(int a) { int n } int fctB(int a) { int n; n = fctA(n) } int main(int argv, char *argc[]) { int m, int n; n = fctA(m); m = fctB(n); n = fctA(100) } Grundlagen der Informatik fctA global a Param. (lokal) n lokal fctB global a Param. (lokal) n lokal main global argv Param. (lokal) argc Param. (lokal) n lokal m lokal Folie 45 Sichtbarkeit und Parameterersetzungen int fctARef(int &a) { int n } int fctBRef(int &a) { int n; n = fctA(n) } int main(char *argv[], int argc) { int m, int n; n = fctARef(m); m = fctBRef(n); n = fctARef(100); // illegal } Grundlagen der Informatik fctARef global a Param. (lokal) n lokal fctBRef global a Param. (lokal) n lokal main global argv Param. (lokal) argc Param. (lokal) n lokal m lokal Folie 46 Überladen von Funktionen In C++ ist es zulässig mehreren Funktionen den gleichen Namen zu geben. Allerdings müssen sich zwei gleichnamige Funktionen in mindestes einem Datentyp in der Parameterliste oder der Anzahl unterscheiden. Diese Eigenschaft wird als überladen von Funktionen bezeichnet Es ist nicht zulässig, dass sich zwei Funktionen nur im Rückgabewert unterscheiden Bsp.: float max(float A[], int n); int Summe(float A[]); float max(float a, float b); float Summe(float A[]); // falsch Operatoren wie +, -, * werden wie Funktionen behandelt z. B. long operator+(const long a, const long b);// entspricht der ganzzahligen Addition Alle Standardoperatoren können überladen werden (nur C++) Grundlagen der Informatik Folie 47 Überladen von Operatoren Beispiel: Multiplikation Skalar mit Vektor struct vector { float elems[n]; } vector operator*(float a, const &vector x) { vector h; for(int i = 0; i < n; i++) { h.elems[i] = a * x.elems[i]; } return h; } vector a, b; float c; b = c * a; // Es wird obiger Operator eingesetzt b = a * c; // illegal Grundlagen der Informatik Folie 48 Rekursive Programme Funktionen können andere Funktionen die bereits deklariert sind aufrufen. Insbesondere können Funktionen auch sich selbst aufrufen. Diese wird als rekursiver Aufruf bezeichnet. Bsp: Berechnung der Fakultät n! = 1 2 .... n (Beachte 0! := 1) int Fakult(int n) { if (n < 0) return -1; // Fehler; if (n == 0) return 1; return Fakult(n-1)*n; } Grundlagen der Informatik Folie 49 Funktionsdeklaration und Module Da sich Funktionen gegenseitig aufrufen können, muß es möglich sein, diese Bekannt zu machen ohne ihren Rumpf anzugeben. Man sprich von einer Funktionsdeklaration. Diese hat die Form: <Rückgabetyp> <FName>(<Parameterliste>); Bsp. void A() { B(); //illegal } void B() { A();//illegal } void B(); void A() { B(); } void B() { A(); } void A() { B(); } void B() { A(); } Grundlagen der Informatik Folie 50 Funktionsdeklaration und Module Große Programme werden meist in sogenannte Module zerlegt. Jedes Modul realisiert eine bestimmte Teilaufgabe Hierbei werden von einem Modul bestimmte benutzerdefinierte Datentypen und Funktionen bereitgestellt. Alle Datentypen und Funktionen des Moduls werden zunächst in einer Art Schnittstelle deklariert. Diese Datei erhält den Namen <Modul>.h (Header-File) Die Definition der Funktionen und eventuell zusätzliche Hilfsfunktionen erfolgt in der Datei <Modul>.cpp (Implementierung) Hierdurch ist es möglich, jedes einzelne Modul getrennt zu übersetzen. Vom Compiler wird hierdurch eine Objektdatei <Modul>.obj erzeugt. Alle Module müssen dann zu einem Gesamtprogramm gebunden werden (Linker) Grundlagen der Informatik Folie 51 Funktionsdeklaration und Module Verhindert ein matrix.h #ifndef MATRIX_H mehrfaches #define MATRIX_H Einkopieren const int n; typedef float Matrix[n][n]; Einkopieren Hauptprogramm (.cpp) void ReadM(Matrix X); void PrintM(Matrix X); #include "matrix.h" Matrix MultM(X, Y); int main (int argc, #endif char **argv) matrix.cpp #include "matrix.h" { Matrix A,B,C; void ReadM(Matrix X) { ReadM(A); // Code zum Einlesen } ReadM(B) void PrintM(Matrix X) { C = MultM(A,B) // Code zun Ausgeben } PrintM(C); Matrix MultM(Matrix X, Matrix Y) return 0; { // Code zur Multiplikation } } Bsp. Grundlagen der Informatik Folie 52 Arbeiten mit Dateien Das FILE-Konzept (benötigte Header-Datei stdio.h) Der vordefinierte Datentyp: FILE * ist ein Zeiger auf eine Dateistruktur. Dateivariable FILE* f Verbindung zwischen Dateivariable und Betriebssystem-Datei Absoluter Pfad f = fopen("C:\\MeinOrdner\\MeineDatei.dat","r+") Relativer Pfad f = fopen("MeineDatei.dat","r+") C: MeinOrdner Modus r w a + MeineDatei.dat zum Lesen; die Datei muß vorhanden sein zum Schreiben, der Inhalt wird gelöscht Datei wird zum Schreiben geöffnet; neue Daten werden angehängt Datei wird zum Lesen und Schreiben geöffnet Grundlagen der Informatik Folie 53 Arbeiten mit Dateien Vordefinierte Dateivariablen stdin Standardeinegabe (z. B. Tastatur) stdout Standardausgabe (z. B. Bildschirm) stderr Standardausgabe für Fehlermeldungen (z. B Bildschirm) Operationen auf Dateien Lesen int fscanf(FILE *f, char *Format-String, <Argumente>) Es wird die Anzahl der umgewandelten Felder zurückgegeben. Am Dateiende wird EOF zurückgegegben int getc(FILE *f) Liest von der Datei ein Zeichen ein. Der Code des Zeichens wird zurückgegeben. fseek(FILE *f, long offset, int origin) Positionieren des Dateizeigers origin: seek_cur, seek_set, seek_end Grundlagen der Informatik Folie 54 Arbeiten mit Dateien Schreiben int fprintf(FILE * f, char* Format-String,<Argumente>) Schreibt den Format-String in die Ausgabedatei f für jede Format-Anweisung wird eine Argument benötigt int putc(int c, FILE * f) Es wird eine Zeichen in die Datei f geschrieben Prinzip: Dateizeiger l l o 1 2 . 3 4 EOF H a Zur Leistungssteigerung wird nicht auf den Dateien direkt sondern über sogenannte Puffer gearbeitet. Puffer sind Bereiche im Hauptspeicher, die eine bestimme Menge von Bytes aufnehmen können. Zur Leerung der Puffer dient die Funktion: int fflush(FILE *f) Grundlagen der Informatik Folie 55 Arbeiten mit Dateien Das Stream-Konzept Benötigte Header-Datei <iostream.h> stream-Klassen <iomanip.h> Formatierung Streams sind Ströme von Bytes. Die Daten können zwischen Programmen oder Peripherie-Geräten ausgetauscht werden. Je nach Anwendungsfall wird eine geeignete Klasse verwendet. Vordefinierte streams cin Standardeingabe (entspricht stdin) cout Standardausgabe (entspricht stdout) cerr Fehlerausgabe (entspricht stderr) Grundlagen der Informatik Folie 56 Arbeiten mit Dateien Lesen von streams >> der Extraktionsoperator Bsp.: int n; cin >> n; Schreiben in streams << der Schreiboperator Bsp.: int n = 5; cout << "n = " << n << endl; // gibt n = 5 aus Grundlagen der Informatik Folie 57 Arbeiten mit Dateien Öffnen und Schließen einer Eingabedatei als Stream istream ein(""C:\\MeinOrdner\\MeineDatei.dat"); //Öffnen oder istream ein; ein.open("MeineDatei.dat") ein.close(); // Schließen Öffnen und Schließen einer Ausgabedatei als Stream ostream aus(""C:\\MeinOrdner\\MeineDatei.dat"); //Öffnen oder ostream aus; aus.open("MeineDatei.dat") aus.close(); // Schließen Grundlagen der Informatik Folie 58 Sortieren von Feldern Es soll ein Algorithmus entwickelt werden, der ganzzahlige Feldelemente eines Feldes F in aufsteigend sortiert. Es soll also gelten F[i] F[k] für i < k (eigentlich nicht fallend) RippleSort Vorgehensweise: (Datenstruktur int F[n]) 1.Schritt: Suche kleinstes Element in der Folge von i = 0 bis n-1 int min = i; for (int k = i; k < n; k++) if (F[k] < F[min]) min = k; 2. Schritt: Vertausche i-tes Element mit dem Kleinsten int h = F[i]; F[i] = F[min]; F[min] = h; 3. Schritt: Wiederhole Schritte 1 bis 3 mit i = i + 1 solange i < n-1 Grundlagen der Informatik Folie 59 Sortieren von Feldern void RippleSort(int F[n]) { for (int i = 0; i < n-1; i++) { int min = i; for (int k = i+1; k < n; k++) if (F[k] < F[min]) min = k; int h = F[i]; F[i] = F[min]; F[min] = h; } } Grundlagen der Informatik Variable n i k min F[0] F[1] F[2] F[3] h Wert 4 undef. 2 1 0 undef. 1 2 3 4 undef. 0 3 1 2 10 3 5 10 3 8 10 8 undef. 10 5 Folie 60 Sortieren von Feldern Handelt es sich um beliebige Felder so kann in C++ ein geeigneter < Operator definiert werden. (In C++ lassen sich alle Standardoperatoren für beliebige Datentypen definieren (Overloading)) Bsp. Namesliste struct Name { CString NName; CString Vorname; CSstring Strasse; } Sortierkriterien A1 < A2 (kommt vor) 1. A1.NName kommt alphabetisch vor A2.NName 2. Bei Namesgleichheit A1.Vorname kommt alphabetisch vor A2.Vorname 3. Sind beide Namen gleich werden die Strassen alphabetisch verglichen 4. Sind alle Einträge gleich ist das Ergebnis falsch Grundlagen der Informatik Folie 61 Sortieren von Feldern Beispiel für die Deklaration eines Operators //benötigte include Datei #include <string.h> bool operator<(const Name &N1, const Name &N2) { int c; if (c = strcmp(N1.NName,N2.NName)) return (c<0)?true:false; if (c = strcmp(N1.VName,N2.VName)) return (c<0)?true:false; if (c = strcmp(N1.Strasse,N2.Strasse)) return (c<0)?true:false; return false; } Grundlagen der Informatik Folie 62 Ein weiterer Sortieralgorithmus MergeSort 1. Schritt Aufteilen Teile etwa in der Mitte Anstatt den gesamten Vektor zu sortieren wird das Problem dadurch reduziert, dass zweimal ein jeweils nur halb so großer Vektor sortiert wird. Das Aufteilen wird solange fortgesetzt bis der Vektor nur noch ein Element enthält. Dieses ist natürlich "sortiert" Grundlagen der Informatik Folie 63 Ein weiterer Sortieralgorithmus 2. Schritt Zusammenfügen zwei sortierte Vektoren < Grundlagen der Informatik Vergleiche die beiden ersten Elemente und füge das Kleinere in den Ergebnisvektor ein. Fahre mit dem jeweils nächsten fort. Folie 64 MergeSort void MergeSort(int Ein[], int Aus[], int left, int right) { int diff = right - left; if (diff > 0) { MergeSort(Ein,Aus,left,left+diff/2); MergeSort(Ein,Aus,left+diff/2+1,right); int p = left, q = left, r = left+diff/2+1; do{ if(Aus[q] < Aus[r]) Ein[p++] = Aus[q++]; else Ein[p++] = Aus[r++]; } while((q <= left+diff/2) && (r <= right)); for( ; q <= left+diff/2; ) Ein[p++] = Aus[q++]; for( ; r <= right; ) Ein[p++] = Aus[r++]; } for(int i = left; i <= right; i++) Aus[i] = Ein[i]; Grundlagen der Informatik } Folie 65 Laufzeitvergleich 0,50000000 0,45000000 0,40000000 0,35000000 0,30000000 RippleSort MergeSort 0,25000000 0,20000000 0,15000000 0,10000000 0,05000000 0,00000000 0 Grundlagen der Informatik 2000 4000 6000 8000 10000 12000 Folie 66 Laufzeitabschätzung void RippleSort(int F[n]) { S0 Zuweisungen for (int i = 0; i < n-1; i++) { S1 Vergleich, fünf Zuweisungen, Inkrement int min = i; for (int k = i+1; k < n; k++) S2 zwei Vergleiche, Inkrement if (F[k] < F[min]) Zuweisung z min = k; int h = F[i]; F[i] = F[min]; F[min] = h; } } Problem: Die Zuweisung in der if-Anweisung wird nicht immer ausgeführt: 1. Schlimmster Fall (worst case): Die Zuweisung wird immer ausgeführt (WCET) 2. Bester Fall (best case): Die Zuweisung wird nie ausgeführt 3. Es wird nach der Wahrscheinlichkeit der Ausführung eine Mittelwert berechnet τ WCET = τ S 0 + n ⋅ (τ S1 + n ⋅ (τ S 2 + τ Z ) ) = (τ S 2 + τ Z ) ⋅ n 2 + τ S1 ⋅ n + τ S 0 Grundlagen der Informatik Folie 67 Laufzeitabschätzung MergeSort Stufen durch die Zerlegung der Eingabefolge In jeder Stufe befinden sich n Elemente Die folgende Berechnung ist nur eine grobe Schätzung In jeder Stufe erfolgen maximal n Vergleiche und 2 n Kopieroperationen Wir können somit den Aufwand für eine Stufe mit n S abschätzen Es gibt insgesamt ld(n) Stufen. In jeder Stufe sollen ein weiterer Aufwand von C entstehen τ WCET = ld (n ) ⋅ (τ C + n ⋅ τ S ) = ld (n ) ⋅ τ C + n ⋅ ld (n ) ⋅ τ S Grundlagen der Informatik Folie 68 Der O-Kalkül (Groß-O-Kalkül) Für ein Programm ist der Rechenzeitaufwand für große Eingaben interessant. Man spricht hier auch vom asymptotischen Verhalten. Gegeben zwei Funktionen f(n) und g(n) f(n) = O(g(n)), falls es ein n0 und ein C gibt mit n > n0 : f(n) < C g(n) Bsp. f(n) = n2 + 10, g(n) = n3 für n > 3 gilt f(n) < g(n) Rechenregeln f(n) + C = O(f(n)) (f(n) Nullfkt.) C f(n) = O(f(n)) am nm a(m-1) n(m-1) +…+a0 = O(nm) Ist der Rechenzeitaufwand für ein Programm O(nm) so nennt man es polynomial beschränkt. Grundlagen der Informatik Folie 69 Ein schwieriges Problem Rucksackproblem Gegeben: eine Menge D = {di | i = 1..n } von Dingen. Jedem di wird ein Volumen V(di) und eine Nützlichkeit N(di) zugeordnet. Weiter gibt es einen Rucksack mit einem maximalen Volumen Vmax. Gesucht: Eine Auswahl A von Dingen, so dass diese in den Rucksack passen und insgesamt die Nützlichkeit maximiert wird, oder formal A⊆D: ∑ V (d ) ≤ V di ∈A i max ! und ∑ N ( d ) = max di ∈A i mögliche Lösung: 1. Bestimme alle Teilmengen A von D 2. Prüfe, ob für eine gegebene Auswahl A das Volumen nicht überschritten wurde. 3. Berechne für jede gültige Auswahl die Nützlichkeit und wähle diejenige mit dem höchsten Wert Problem: es gibt 2n Teilmengen Grundlagen der Informatik Folie 70 Laufzeituntersuchungen Eine Basisoperation dauert 1 ns = 10-9s n n ld(n) n2 n3 2n 10 3,32193E-08 0,0000001 0,000001 0,000001024 20 8,64386E-08 0,0000004 0,000008 0,001048576 40 2,12877E-07 0,0000016 0,000064 1099,511628 80 5,05754E-07 0,0000064 0,000512 1,20893E+15 ca 920 Mio Jahre 160 1,17151E-06 0,0000256 0,004096 1,4615E+39 320 2,66302E-06 0,0001024 0,032768 2,13599E+87 640 5,96603E-06 0,0004096 0,262144 4,5624E+183 Algorithmen gelten als effizient, falls sie polynomial beschränkt sind. In der Praxis O(n2) ausnahmsweise O(n3) . Grundlagen der Informatik Folie 71 NP-harte Probleme Bei einigen Problemen sind für (deterministische) Rechner nur Algorithmen mit exponentieller Laufzeit bekannt. Es handelt hierbei um so genannte NP-harte Probleme. Beispiele Rucksackproblem Travelling Salesman Suche nach einer optimalen Route bei einer Rundreise Steiner-Baum-Problem kürzeste Verdrahtung von mehreren Punkten Scheduling Belegungsplanung von Maschinen / Ablaufplanung Graphen-Probleme Bei diesen Problemen kann von einem Rechner eine optimale Lösung meist nur angenähert werden. Grundlagen der Informatik Folie 72 Laufzeitoptimierungen Ein Programm sollte möglichst kurze Laufzeiten haben: – Optimierung des Algorithmus Grundsätzlich sollte zunächst für ein gegebenes Problem ein möchlichst effizienter Algorithmus verwendet werden mit O(nk), wobei k so klein als möglich zu wählen ist. Eventuell kann es auch nützlich sein den mittleren Aufwand abzuschätzen und gegebenenfalls zu optimieren – Feinoptimierung Es gilt üblicherweise die Regel, dass 80 – 90 % der Laufzeit in 10 -20 % der Codezeilen verbraucht wird. Durch Analyse des Programms (Profiling) sollten diese Stellen untersucht und falls möglich verbessert werden durch • Umformulierung der Anweisungen • Beachtung der Rechnerarchitektur (interne Parallelität, Cache) • Verwendung von Assembler? Durch Feinoptimierung kann die Laufzeit nur um einen konstanten Faktor reduziert werden. Grundlagen der Informatik Folie 73 Einige Aspekte der Sotwaretechnik Gütekriterien für Programme – Wartbarkeit Programme sollten so geschrieben werden, dass nachträglich (auch von anderen Personen) Änderungen und Anpassungen leicht durchführbar sind. – Portabilität Programme sollten möglichst ohne Änderung auf verschiedenen Plattformen lauffähig sein. Auch bei höheren Programmiersprachen ist diese Eigenschaft durch einen geeigneten Programmierstiel sicherzustellen. – Fehlerfreiheit Das Programm muss durch ausreichende Tests validiert werden. Allerdings kann durch Test nur die Anwesenheit von Fehlern erkannt werden, nicht die Fehlerfreiheit. Verfikation. Es gilt die Regel, dass für den Test die gleiche Zeit wie für Codierung einzuplanen ist. Grundlagen der Informatik Folie 74 Softwareentwicklung Tätigkeiten Problemanalyse Dokumente Pflichtenheft Leistungsmerkmale Testspezifikation Systementwurf Modularisierung Codierung Test Modultest Integrationstest Abnahme Grundlagen der Informatik Modulbeschreibungen Modultestspezifikation Programmdokumentation Testdokumentation Folie 75 Softwareentwicklung Prototypentwicklung Grundlagen der Informatik Folie 76 Aufgabe 1 Erstellen Sie ein C-Programm, das zwei Matrizen A und B einliest und die Matrix C = A B ausgibt. Die Matrizenmultiplikation von zwei n n Matrizen ist definiert als n ci, j = ∑ ai,k ⋅ bk,j k =1 Das Programm soll für beliebiges n gelten. Verwenden Sie zu Testzwecken die Deklarationen const int n = 2; float A[n][n], B[n][n], C[n][n]; Die Ein/Ausgabe von Gleitkommawerten wird in der Übung behandelt. Diese gestalten sich ähnlich wie Festkommawerte. Grundlagen der Informatik Aufgabe 1 Entwickeln Sie einen Algorithmus für folgendes Problem: Gegeben ist die Funktion y(t) = exp(- t) sin( t) Die Funktion soll als Diagramm durch das Ausdrucken von ‚*‘ ausgegeben werden Hierbei liegt die y-Achse horizontal, die t-Achse vertikal. Aufgabe 2 Erstellen Sie ein C-Programm, das eine Tabelle aus 18 Fußballvereinen einlesen kann. Ein Verein wird durch die Daten Vereinsname (max. 256 Zeichen), Punkte geschossene Tore erhaltene Tore beschrieben. Die eingelesenen Daten sollen als formatiert Ausgegeben werden. Hinweis: Erstellen Sie einen Verbunddatentyp für die Vereinsdaten. Die Tabelle kann dann als Feld von Vereinsdaten dargestellt werden. Das Einlesen eines Strings mit der Deklaration char str[256]; // Deklaration erfolgt durch scanf("%s",str); // Platzhalter %s Beachte KEIN & vor der Variable Aufgabe 4 Erstellen Sie ein C-Programm, das eine Tabelle aus 18 Fußballvereinen sortiert. Ein Verein wird durch den Namen, die Punkte, die Anzahl der geschossenen Tore sowie die Anzahl der erhaltenen Tore charakterisiert. Ein Verein V1 soll vor einem Verein V2 in der Tabelle stehen falls 1. V1 mehr Punkte als V2 hat. 2. Bei gleicher Punktzahl, falls V1 eine größere Tordifferenz als V2 hat. Die Tordifferenz ist die Differenz aus geschossenen minus erhaltenen Toren. 3. Bei gleicher Tordifferenz, falls V1 mehr geschossene Tore als V2 hat. 4. Sind alle vorherigen Werte gleich so steht der alphabetisch kleinere vorn. (Identische Einträge sollen ausgeschlossen sein) Hinweis: die Funktion int strcmp(const char* s1, const char* s2) liefert -1 falls s1 alphabetisch vor s2 kommt 0 bei Gleichheit +1 falls s1 alphabetisch nach s2 kommt Header-Datei <string.h> Aufgabe 5 Verändern Sie das Programm aus Aufgabe 4 so, daß die Daten für die Fußballtabelle aus der Datei "Liga.dat" eingelesen werden. Hierbei besteht die Eingabe aus 18 Zeilen, d. h. eine Zeile pro Verein Jede Zeile hat den Aufbau <Name des Vereins> ; <Punkte> ; <Gesch. Tore> ; <Erhalt. Tore> \n