C und C++ (CPP) 1. Grundlegende Programmelemente Prof. Dr. Marc Rennhard Institut für angewandte Informationstechnologie InIT ZHAW Zürcher Hochschule für Angewandte Wissenschaften [email protected] Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 1 1 Ablauf • Hello World in Java und C • Sprachelemente, Kommentare, Variablen, einfache Datentypen, Literale • Deklarationen, Operatoren, Ausdrücke • Kontrollstrukturen • Input/Output Funktionen der Standard Library • Komplexe Datentypen: Aufzählungstyp, Strukturen Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 2 2 Ziele • Sie erkennen, dass die grundlegende Syntax von C sehr viele Ähnlichkeiten mit der von Java aufweist • Sie sehen auch, dass C gewisse Eigenheiten aufweist, welche in Java nicht zu finden sind • Sie kennen und verstehen die grundlegenden Programmelemente, die in C verwendet werden (Kommentare, Variablen, einfache und komplexe Datentypen, Literale, Deklarationen, Operatoren, Ausdrücke, Kontrollstrukturen) • Sie kennen die wichtigsten Input/Output Funktionen der Standard Library und können diese einsetzen Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 3 3 Hello World (1) • Java: /* HelloWorld.java */ public class HelloWorld { // Hauptprogramm public static void main(String[] args) { System.out.println("Hello World in Java"); } } • C: /* helloworld.c */ #include <stdio.h> /* Hauptprogramm */ int main(void) { printf("Hello World in C\n"); } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 4 4 Übung zu Hello World • Welche Unterschiede und Gemeinsamkeiten erkennen Sie zwischen den Hello World Programmen in Java und C? • Ähnlichkeiten: • Funktion/Methode main als Einstiegspunkt • Ausgabe des Strings mit "Hello World" • Geschweifte Klammern (Blöcke), Abschluss eines Befehls mit Semikolon (;) • Einrücken des Codes • Unterschiede: • #include <stdio.h> in C • Klasse in Java • Kommentar mit // in Java und /*…*/ in C • Parameterliste von main in Java mit Argument args[ ] und in C mit void • Rückgabewert von main in Java void und in C int • Ausgabe in Java mit System.out.println und mit printf in C • \n in printf bei C Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 5 5 Überblick über C • C ist eine prozedurale Programmiersprache (≠ objektorientiert) • Keine Klassen • Funktionen statt Methoden • Ein C Programm besteht im Wesentlichen aus Funktionen • Jedes C-Programm hat eine main-Funktion Einstiegspunkt ins Programm • Es kann beliebig in weitere Funktionen verzweigt werden • Wird die main-Funktion verlassen, so terminiert das Programm • Ein C-Programm soll die Endung .c haben (Java: .java) • Mit einem C-Compiler (zB gcc) wird ein ausführbares Programm erzeugt (–o spezifiziert den Namen des Programms, default a.out / a.exe) • $> gcc –o helloworld helloworld.c • Dieses Programm kann „direkt“ (ohne virtual machine) gestartet werden • $> helloworld Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 6 Der Java-Compiler (javac) generiert .class-Files. Diese sind nicht direkt, sondern nur via die Java Virtual Machine (JVM) mit dem Befehl java ausführbar. Dafür sind die class-Files portable, sofern eine JVM vorhanden ist. Ein C-Programm muss für jedes System neu kompiliert werden, und manchmal müssen sogar Teile des Source-Codes angepasst werden. Das mit gcc kompilierte C-Programm erhält nicht den Manen des Quellcodedatei (hier: helloworld.c). Mit der –o Option kann der Name angegeben werden, den das ausführbare Programm erhalten muss. Geben Sie nichts an, so erhält das ausführbare Programm je nach Plattform per Default den Namen a.out oder a.exe. 6 Sprachelemente • C Programme bestehen aus Schlüsselwörtern, Namen, Konstanten, Strings, Operatoren und anderen Trenn- und Sonderzeichen • Trennzeichen sind Space, Tab und Zeilenende • Ein C-Programm darf die folgenden Zeichen beinhalten • Klein-und Großbuchstaben: a-z, A-Z • Ziffern: 0-9 • Sonderzeichen: + - * / \ = , . ; ? " # % & _ ' < > ( ) [ ] { } | ^ ~ @ : • Schlüsselwörter: auto break do double goto if signed sizeof unsigned void case else int static while char enum long struct volatile continue extern register switch const float return typedef default for short union Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 7 Schlüsselwörter sind reserviert und dürfen nicht als Namen für zB Variablen verwendet werden. 7 Kommentare, Variablen- und Funktionsnamen • Kommentare können wie folgt in den Code eingefügt werden: • /* Dieser Kommentar kann sich über mehrere Zeilen erstrecken */ • /* Natürlich kann er auch nur eine Zeile lang sein */ • // Dieser Kommentar existiert in Java und in C++, nicht aber in C • // Die Begrenzung erfolgt hier durch das Zeilenende • Variablen- und Funktionsnamen haben folgende Restriktionen: • Enthalten Zahlen 0…9; Buchstaben a…z und A…Z, und das Zeichen _ • Gross-/Kleinschreibung wird beachtet; d.h. Hallo und hallo ist nicht dasselbe • Das erste Zeichen muss ein Buchstabe oder _ sein • Darf keines der durch die Sprache reservierten Wörter sein (int, while, return etc.) • Beispiele: i, count, MAX_LENGTH, maxValue, getMax(...) Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 8 Konvention: Einfache Variablennamen sollen prinzipiell aus Kleinbuchstaben bestehen (i, count, limit...). Für zusammengesetzte Namen verwendet man _ (max_value) oder einzelne Grossbuchstaben (maxValue). 8 Einfache (primitive) Datentypen • C kennt nur 4 elementare Datentypen: char int float double 1 4 4 8 byte bytes bytes bytes -128 bis 127 -231 bis 231-1 -3.4·1038 bis 3.4·1038 -1.79·10308 bis 1.79·10308 • Zusätzlich gibt es Qualifier (Attribute): unsigned char unsigned int short [int] unsigned short [int] long [int] unsigned long [int] long double 1 byte 4 bytes 2 bytes 2 bytes 8 bytes 8 bytes 12 bytes 0 bis 255 0 bis 232-1 -32768 bis 32767 0 bis 65535 -263 bis 263-1 0 bis 264-1 -1.2·104932 bis 1.2·104932 • Die Wertebereiche sind hardwareabhängig, die obigen Werte sind typisch für eine heute übliche 32-Bit Architektur Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 9 Je nach unterliegender Hardware werden verschiedene Anzahl Bytes für ein int verwendet. Die oben angegeben Grössen bezeichnen die heute auf 32-Bit Rechnerarchitekturen typischen Grössen für die Datentypen. Auf solchen Rechnern wird ein int also meist durch 4 Bytes dargestellt, ein short ist meist 2 Bytes und ein long meist 8 Bytes. Die einzige Bedingung, welche C macht, ist dass ein short sicher nicht länger als ein int und ein long sicher nicht kürzer als ein int sein darf. In Java ist ein int immer 4 Bytes, ein short immer 2 Bytes und ein long immer 8 Bytes. Die C Standard Library bietet die Möglichkeit, die Grösse eines bestimmten Datentyps auf dem gegebenen System zu erhalten (via Headerdateien float.h und limits.h). Unsigned bedeutet nur positive Zahlen (beginnend mit 0). Default ist immer nicht unsigned. In Java ist immer alles implizit signed. Im Zusammenhang mit short und long kann das int weggelassen werden, d.h. short int ≡ short, long int ≡ long etc. Allgemeine Regel in C: Aus Präzisionsgründen immer mit double statt float arbeiten. 9 Literale (1) • Literale sind im Code eingefügte, unveränderliche Werte • Ganze Zahlen (short, int, long) • Zahlen, die mit 1…9 beginnen, werden dezimal interpretiert; zB 3987 • Zahlen, die mit 0 (Zahl Null) beginnen, werden oktal interpretiert: zB 037 • Zahlen, die mit 0X oder 0x beginnen, werden hexadezimal interpretiert; zB 0x23 • Per Default wird eine Dezimalzahl als int interpretiert, eine oktale/hexadezimale Zahl als unsigned int • Ist die Zahl für einen int zu gross, wird sie als long interpretiert • Soll eine Zahl explizit als long interpretiert werden, so wird l oder L angehängt: 35000l (oder 35000L) 0104270l 0x8868l Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 10 Die explizite Angabe von L oder l bei einer ganzen Zahl kann Sinn machen. Wenn Sie z.B. mehrere int-Literale addieren und irgendwann das Zwischenresultat grösser wird als was mit einem int dargestellt werden kann, dann wird das Resultat falsch sein. Wenn Sie die Zahlen aber explizit als long definieren, dann wird diese Problem nicht auftauchen, ausser Sie überschreiten auch den Wertebereich eines long. 10 Literale (2) • Gleitkommazahlen (float, double) • Weisen einen Dezimalpunkt (zB 3.1415) oder einen Exponenten (zB 1e-2) auf • Per Default vom Typ double, kann mit f oder F explizit als float bezeichnet werden 3.1415f (oder 3.1415F) • Einzelne Zeichen (char) • Werden durch ' ' eingeschlossen; zB 'A' • Gespeichert wird der entsprechende ASCII-Wert (1 Byte) des Zeichens (A = 65) • Spezielle Zeichen werden mit \ gebildet; zB \n (new line), \t (horizontal tab), \\ (backslash), \' (single quote), \" (double quote), \0 (NUL) • Ein einzelnes Zeichen kann auch durch den ASCII-Wert (dezimal, oktal oder hexadezimal) ausgedrückt werden: 65, \101, \x41 entsprechen alle dem ASCII Zeichen mit dem Wert 65 (= A) • Java: Ein char hat die Länge 2 Bytes, Unicode-Codierung Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 11 11 ASCII-Table Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 12 Dies sind die originalen ASCII-Zeichen (0 – 127); dargestellt mit 1 Byte, wobei das führende Bit immer = 0 ist. Es gibt auch Erweiterungen von 128 – 255 (führendes Bit = 1), die zB die Umlaute beinhalten. 12 Literale (3), Symbolische Kostanten • Strings • Durch Anführungszeichen eingeschlossene Zeichenfolgen; zB "ZHAW" • Es existiert kein Datentyp String; ein String wird intern als ein Array von Characters (char) repräsentiert (also die ASCII-Werte der einzelnen Zeichen) • Strings werden automatisch durch das ASCII Zeichen NUL abgeschlossen "ZHAW" entspricht den fünf ASCII Zeichen 'Z' 'H' 'A' 'W' '\0' Der Speicherplatz in Bytes, den ein String benötigt, ist deshalb immer die Anzahl Zeichen + 1 Welchen numerischen Wert haben die ASCII Zeichen, mit welchen der String "ZHAW" intern abgespeichert wird? 90 – 72 – 65 – 87 – 0 • Symbolische Konstanten • • • • Machen dann Sinn, wenn die gleiche Konstante mehrfach gebraucht wird Werden mit #define am Anfang des Programms gesetzt #define MAX_LENGTH 1000 (kein ; am Ende!) Während des Kompilierens wird jedes Auftreten von MAX_LENGTH im Code mit dem entsprechenden Wert (1000) ersetzt • Also: int length = MAX_LENGTH; int length = 1000; Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 13 Das ASCII-Zeichen NUL ist das erste ASCII-Zeichen der ASCII-Tabelle und besteht aus dem numerischen Wert 0 (Byte: 00000000). Sämtliche Befehle, die mit einem # beginnen (also zB #define), sind Präprozessorbefehle (siehe Kapitel über Modulare Programmierung). Auch wenn dies sie Sprache nicht verlangt, ist es guter Programmierstil, mit #define definierte Konstanten mit Grossbuchstaben zu bezeichnen (zB MAX_LENGTH). 13 Deklarationen (1) • Variablendeklaration (es gibt in C/C++ keine Default-Werte bei der Initialisierung, ausser bei statischen Variablen!) double hoehe; int laenge, breite; double r, radius = 15.0; • Konstantendeklaration const double pi = 3.14159; • Der Wert von pi kann einmal initialisiert, dann aber nicht mehr verändert werden • Typdeklaration typedef int Index; • Erlaubt die Definition von neuen Namen für Datentypen; hier ist zB Index ein anderer Name (Synonym) für int, deshalb sind die folgenden Variablendeklarationen äquivalent: int i; Index i; Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 14 Im Gegensatz zu Java erhält eine Variable bei der Initialisierung keinen Default-Wert. Die Ausnahme sind statische Variablen, siehe dazu das nachfolgende Kapitel. hoehe, laenge und bretet im Beispiel oben erhalten bei der Deklaration diejenigen Werte, die gerade in den entsprechenden Speicherzellen, die alloziert werden, stehen. Will man bewusst einen initialen Wert 0 (wie bei Java), so muss man dies selbst tun mit double hoehe = 0. Konvention: Mit typedef definierte Typen haben einen Grossbuchstaben am Anfang (zB Index). Unterschied const und #define: Mit const wird eine „richtige“ Variable alloziert, die einfach nicht mehr verändert werden kann. Entsprechend stehen auch alle Typen (auch benutzerdefinierte Typen, z.B. Strukturen in C oder Klassen in C++) zur Verfügung und der Compiler kann ein sauberes Type-Checking durchführen. Ausserdem gelten die Sichtbarkeitsregeln wie bei normalen Variablen (siehe späteres Kapitel); eine mit #define generierte Konstante ist immer überall sichtbar. Der programmiertechnisch saubere Ansatz ist deshalb der, für Konstanten nach Möglichkeit den obigen Ansatz mit const zu verwenden (insbesondere wenn die Konstante nur innerhalb z.B. einer Funktion vorhanden sein sollten). Dennoch sieht man häufig auch den Ansatz mit #define, insbesondere wenn die Konstante in mehreren Funktionen gebraucht wird. 14 Deklarationen (2) • Variablen werden meist innerhalb von Funktionen deklariert • Ausnahme: globale Variablen • Beispiel: Funktion, die km/h und zugehörige m/s-Werte auf dem Bildschirm ausgibt: void kmhToMs(void) { int kmh; double ms; const double ratio = 3.6; /* int-Variable */ /* double-Variable */ /* Konstantes Verhaeltnis */ for (kmh = 0; kmh <= 300; kmh = kmh + 20) { ms = kmh / ratio; printf("%d -> %f\n", kmh, ms); } } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 15 15 Operatoren • Bis auf wenige Ausnahmen (>>> Operator in Java, Pointeroperatoren in C) gleich wie in Java • • • • • • • Arithmetische Operatoren: + - * / % (modulo) Relationale Operatoren: > >= < <= Logische Operatoren: && || Gleichheitsoperatoren: == != Negationsoperator: ! Increment / Decrementoperator: ++ -Bitoperatoren: & (AND) | (OR) ^ (XOR) << (bit shift left) >> (bit shift right) ~ (Einerkomplement) • Zuweisung: = += -= *= /= %= &= ^= |= <<= >>= • Conditional Expression: ? : • Referenzoperator, Dereferenzoperator: & * Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 16 Sind die beiden Operanden bei der Division ( / ) int-Werte, so wird das Resultat auf einen ganzzahligen Wert abgerundet. Hier wird konsequent abgerundet, 5/3 (= 1.667) wird also zB zu 1. In Java bedeutet der >>> Operator einen „Bit-Shift nach rechts, wobei links eine 0 eingefüllt wird“. Der normale „Bit-Shift right“ Operator füllt links entweder eine 0 oder eine 1 ein, und zwar immer so, dass das Vorzeichen der Zahl nach dem Shift das gleiche wie vor dem Bit-Shift ist. 16 Order Precedence and Associativity Symbol Type of Operation Associativity () [] . –> Expression Left to right ! ~ ++ -- + - * & (type) sizeof Unary Right to left * / Multiplicative Left to right + – Additive Left to right << >> Bitwise shift Left to right < <= > >= Relational Left to right == Equality Left to right & Bitwise-AND Left to right ^ Bitwise-exclusive-OR Left to right | Bitwise-inclusive-OR Left to right && Logical-AND Left to right || Logical-OR Left to right ? : Conditional-expression Right to left = , % != += -= *= /= %= &= ^= |= <<= >>= Simple and compound ass. Sequential evaluation Right to left Left to right Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 17 Die Tabelle stammt aus B. Kernigham, D. Ritchie, The C Programming Language. Prentice Hall, 1988, 2nd Edition. Left to right bedeutet, der Ausdruck wird von links nach rechts ausgewertet (zuerst die linke Seite, dann die rechte Seite), zB 5 – 3 bedeutet „5 (left) minus 3 (right)“. Right to left ist das Umgekehrte, zB a = 7 bedeutet „7 (right) wird a (left) zugewiesen“. Klarer wird das in einem zusammengehängten Ausdruck, zB a = 5 – 3: = ist right to left, also wird zuerst 5 – 3 ausgewertet und das Resultat erst dann a zugewiesen. Oft ist es einfacher, mit Klammern ( ) Klarheit zu verschaffen, auch wenn dies nicht nötig ist. Die wenigsten Programmierer haben die Tabelle oben genau im Kopf... 17 Ausdrücke • Ein Ausdruck ist die Verbindung von Operanden mit Operatoren: • 1 + 3, u = 7 * r etc. • Ausdrücke müssen nicht zugewiesen werden • (u * 100 + v); ist eine legale Anweisung, die nichts bewirkt • In Ausdrücken werden alle numerischen Typen implizit in den „grössten“ Typ gewandelt • 3/2 int / int grösster Typ = int bleibt bei int / int (ganzzahlige Division, Rest wird gestrichen) Resultat = 1 (Typ int) • 3.0/2 double / int grösster Typ = double wird zu double / double Resultat = 1.5 (Typ double) • Der Zuweisungsoperator (=) bewirkt, dass der rechte Ausdruck in den Typ der links stehenden Variablen gewandelt wird • double r; r = 3/2; • int i; i = 3.1415; • int i; i = 2.99 + 3; /* ergibt r = 1.0 */ /* ergibt i = 3 */ /* ergibt i = 5 */ • Keine Warnungen wie „loss of precision“ wie in Java (gilt generell)! Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 18 Werden int und double im gleichen Ausdruck verwendet, muss man immer aufpassen, dass keine Rechenfehler passieren. Das Beispiel oben double r; r = 3/2; ergibt als Resultat 1.0, weil durch die höhere „Presedence“ des Divisionsoperators zuerst der Ausdruck 3/2 ausgewertet wird (= 1) und erst dann das Resultat der Variablen r zugewiesen wird. Es spielt keine Rolle, ob r ein int oder ein double ist, denn die „Präzision“ ist bei der Division 3/2 unwiderrufbar verloren gegangen. Um das korrekte Resultat zu erhalten, muss man also r = 3.0/2; schreiben (oder auch 3/2.0 oder 3.0/2.0), womit die Division als Division zweier double-Typen behandelt wird. 18 Übung zu Ausdrücken • Geben Sie jeweils die Werte der Variablen nach der Ausführung der einzelnen Programmzeilen an /* expression.c */ int main(void) { double x, y = 3.0; int i, j = 4; i x x i i i x = 2.5 + y; = 5 * i / 3; = 5.0 * i / 3; += j; = ++j; = j++; = 3 + (y = i + 5.0); /* x = ? /* i = ? /* /* /* /* /* /* /* i x x i i i x = = = = = = = y = 3.0 j = 4 5 8.0 8.333333 9 5 j = 5 5 j = 6 13.0 y = 10.0 */ */ */ */ */ */ */ */ */ } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 19 19 Type Casts • Ein type cast ist eine explizite Konvertierung von einem Typ in einen anderen • Es gibt verschiedene Gründe, eine solche Konvertierung durchzuführen: • Wandeln einer Gleitkommazahl in einen Integer Wert und umgekehrt • Wandeln eines void-Pointers in einen Pointer auf einen bestimmten Datentyp • Wandeln eines Zeigers auf eine Instanz der Basisklasse in einen Zeiger auf eine abgeleitete Klasse (C++) • Eine Art in C, zwei gleichwertige Arten in C++: • var = (type) expression • var = type(expression) /* Operator, C/C++, Java */ /* Funktion, nur C++ */ • Beispiele: • double d; int i = 5; d = i / 3; • double d; int i = 5; d = (double) i / 3; • double d; int i = 5; d = double(i) / 3; /* d = 1.0 */ /* d = 1.6667 */ /* d = 1.6667 (nur C++) */ Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 20 20 Kontrollstrukturen (1) • Generell: wie in Java • Kontrollstrukturen können verschachtelt werden • Einfache Anweisung • wird mit einem Semikolon (;) abgeschlossen c = a + b; printf("Hello World\n"); • Block • Zusammenfassung von Anweisungen mittels geschweiften Klammern • Variablen müssen in C (nicht aber in C++ oder Java) am Anfang eines { Blocks deklariert werden! int a = 5, b, temp; b = a; temp = b; } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 21 Verschachtelung von Kontrollstrukturen bedeutet, dass innerhalb einer Kontrollstruktur eine weitere aufgerufen werden kann, zum Beispiel: int i, even = 0; for (i = 0; i < 10; i++) { if (i % 2 == 0) { even++; } } Mit sauberem Einrücken des Codes wird die Lesbarkeit signifikant erhöht! 21 Kontrollstrukturen (2) • If-Else / Else-If • Ermöglicht Verzweigungen result = -1; if (n >= 0) { result = 1; } if (n >= 0) { result = 1; } else { result = -1; } if (n > 0) { result = 1; } else if (n == 0) { result = 0; } else { result = -1; } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 22 Bei einzelnen Anweisungen in einem if, else if oder else können die geschweiften Klammern auch weggelassen werden. Dies gilt auch für die nachfolgenden Kontrollstrukturen. Oft ist die konsequente Verwendung von Klammern jedoch übersichtlicher. Im Zusammenhang mit if gibt es noch einen Operator, den man oft vergisst: Den Conditional Operator (? :). Damit kann man ein if-Statement sehr kompakt schreiben: result = ((booleanExpression) ? value1 : value2) Beispiel: int x, y, result; ... If (x > y) { result = 5; } else { result = 7; } ist identisch zu: int x, y, result; ... result = ((x > y) ? 5 : 7); 22 Kontrollstrukturen (3) • For-Schleife • Ermöglicht das wiederholte Durchlaufen eines Programmabschnitts int sum = 0; int max = 5; int i; for (i = 1; i <= max; i++) { sum += i; /* oder sum = sum + i */ } /* Das geht nicht in C (erst ab C99): for (int i = 1; i <= max; i++) {... */ Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 23 Eine for-Schleife hat in der Klammer (...) drei Teile: Die Initialisierung (i = 1) wird einmal am Anfang ausgeführt. Die Bedingung (i <= max) wird vor jedem Durchlauf getestet; ist sie false, so wird die Schleife verlassen. Die Aufdatierung (i++) wird immer am Ende eines Durchlaufs ausgeführt. 23 Kontrollstrukturen (4) • While- und Do-While-Schleifen • Ermöglicht das wiederholte Durchlaufen eines Programmabschnitts int sum = 0; int max = 5; int i = 1; while (i <= max) { sum += i; i++; } /* oder */ do { sum += i; i++; } while (i <= max); Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 24 Die Do-While Schleife wird immer mindestens einmal durchlaufen, da der Test erst nach einem Durchlauf geschieht. In allen drei Schleifen kann continue; verwendet werden, um einen Schleifendurchlauf abzubrechen und direkt zum Ende der Schleife zu springen. Die Schleife wird aber nicht verlassen, sondern – wenn die entsprechende Schleifenbedingung true ist - erneut durchlaufen. 24 Kontrollstrukturen (5) • Switch-Anweisung • Ermöglicht individuelles Reagieren auf verschiedene Werte einer Variable switch (n) { case 1: result = 1; break; case 2: case 3: case 4: result = 10; break; default: result = 0; break; } • break ist notwendig, da sonst weitere cases abgearbeitet werden! • Was würde im Beispiel oben ohne alle breaks passieren? Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 25 break kann auch bei for, while und do-while Schleifen verwendet werden und bewirkt, dass die Schleife direkt verlassen wird. 25 Wo ist denn der Datentyp boolean? • In C gibt es keinen Datentyp boolean! (in C++ gibt es bool) • Regel: ein Ausdruck gilt als false, wenn er = 0 ist; sonst gilt er als true • In beiden Beispielen wird die while-Schlaufe ausgeführt, so lange x > 0 ist: int x = 10; while (x > 0) { x--; } int x = 10; while (x) { x--; } • Der Ausdruck muss dabei kein ganzzahliges Resultat produzieren • while(1){...} while(1.5){...} while(-3.1415){...} etc. in C... • ...entsprechen alle while(true){...} in Java Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 26 Für eine Endlosschleife schreibt man in C üblicherweise while (1) {... 26 Input/Output (1) • Die Sprache C enthält eine Standard Library; enthält unter anderem Input/Output Funktionen • Werden mit #include <stdio.h> am Anfang eines Programms eingebunden • Funktionen für einfachen Input/Output: • puts("Hello"); • putchar('A'); • c = getchar(void); /* Ausgabe des Strings Hello auf Standard Output */ /* Ausgabe des Zeichens A auf Standard Output */ /* Einlesen eines Zeichens von Standard Input */ • Funktionen für formatierte Ausgabe: • Generell: printf(format-string, arg1, arg2,...); • printf("Hello World\n"); /* Ohne weitere Argumente */ • printf("The sum of %d and %d is %d\n", a, b, a+b); /* printf mit 3 Argumenten, wobei a+b innerhalb der Funktion ausgewertet wird; %d bedeutet, dass a, b und a+b als int interpretiert werden */ • Die %d nennt man Konvertierungsoperatoren Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 27 #include in C/C++ ist vergleichbar mit import in Java. Mit #include werden generell Teile der C Standard Library eingebunden; im Fall hier die Input/Output Funktionen. Jedes laufende C Programm (auch Java oder ganz generell jedes Programm) kennt Standard Input, Standard Output und Standard Error. Per default ist Standard Input die Tastatur; Standard Output und Standard Error entspricht der Konsole auf dem Bildschirm. Beim Start eines Programms kann Standard Input/Error/Output sehr einfach umgelenkt werden. Ein Programm mit dem Namen helloworld kann seinen Standard Output zB in das File out umlenken: ./helloworld > out 27 Input/Output (2) • Konvertierungsoperatoren: • %d, %i (int); %u (unsigned int) • %c (char) • %s (char *, Zeichen des Strings werden ausgegeben bis \0 gefunden wird) • %f (double, float) • Bei Zahlen können zwischen dem % und dem Konvertierungszeichen die minimale Länge m (Anzahl Zeichen) des Outputs und die Anzahl Dezimalstellen d angegeben werden: %m.df • • • • double d = 5.12345; printf("%f", d); printf("%.3f", d); printf("%10.3f", d); /* Output: 5.123450 (default d=6) */ /* Output: 5.123 */ /* Output: 5.123 */ Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 28 Ein falsches Konvertierungszeichen wird meist wirren Output generieren, weil dann das Programm versucht, zB ein int-Wert als double zu interpretieren. 28 Input/Output (3) • Funktion für formatierte Eingabe: • Generell: scanf(format-string, arg1, arg2,...); • Beispiel: Einlesen von 3 Werten in die 3 int Variablen day, month und year: • scanf("%d%d%d", &day, &month, &year); • Achtung: der Adressoperator & ist wichtig (siehe folgende Vorlesung über Pointer) • Einlesen auf der Kommandozeile: 24 12 2004<return> • Oder auch 24<return> 12<return> 2004<return> • Erst durch die Eingabe des letzten Return werden die Zeichen aus dem Eingabebuffer gelesen • Man beachte: auch das letzte Return-Zeichen (\n) wird in den Buffer eingelesen und steckt nach dem Zurückkehren der scanf-Funktion noch im Buffer gegebenenfalls mit einem einzelnen getchar() auslesen! Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 29 Wird mit scanf ein double eingelesen, muss der Konvertierungsoperator %lf (statt %f) verwendet werden. Beim Schreiben mit printf funktioniert %f auch für double. 29 Übung zu Formatiertem Output • Komplettieren Sie das vorgegebene Programm, welches die Fläche (= pi * r2) eines Kreises für r = 5, 10, 15 und 20 berechnet und ausgibt • Gewünschter Output: Radius Radius Radius Radius = 5 -> Flaeche = 10 -> Flaeche = 15 -> Flaeche = 20 -> Flaeche = 78.56 = 314.26 = 707.08 = 1257.04 • Programm: /* outformat.c */ # include <stdio.h> int main(void) { const double pi = 3.1415926535; int r; for (r = 5; r <= 20; r += 5) { printf("Radius = %2d -> Flaeche = %7.2f\n", r, pi * r * r); } } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 30 30 Komplexe Datentypen (1) - Aufzählungstyp • Der Aufzählungstyp erlaubt die Definition einer Liste von konstanten intWerten enum wochentage {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag}; • Dies weist den Tagen die Werte Montag = 0… Sonntag = 6 zu • Werte können auch explizit gesetzt werden: enum frucht {Apfel = 5, Birne = 10, Zitrone = 15}; enum frucht {Apfel = 5, Birne, Zitrone}; /* Birne = 6, Zitrone = 7 */ enum frucht {Apfel, Birne = -1, Zitrone}; /* Apfel = 0, Birne = -1, Zitrone = 0; Werte müssen nicht unterschiedlich sein! */ • Die konstanten Werte in der Liste können in Ausdrücken verwendet werden enum frucht {Apfel, Birne, Zitrone}; int essen = Apfel; essen = Birne + Zitrone; /* essen = 0 */ /* essen = 3 */ • Seit Java 1.5 gibt es enum auch in Java Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 31 Der erste Wert einer Aufzählung wird per Default zu 0; die weiteren Werte zu 1, 2, 3 etc. Wird irgendeiner der Werte explizit auf n gesetzt, so werden die nachfolgenden Werte zu n+1, n+2 etc. Der Name (zB wochentage oder frucht in den Beispielen oben) für einen Aufzählungstyp nach dem enum ist optional, dennoch wird er häufig verwendet um anzuzeigen, wozu der Aufzählungstyp überhaupt verwendet wird. Zusätzlich kann dieser Name auch dafür verwendet werden, Variablen von diesem enum-Typ zu deklarieren, wie auf der nächsten Folie aufgezeigt wird. 31 Komplexe Datentypen (2) - Aufzählungstyp • Man kann auch Variablen vom Typ eines Aufzählungstyp deklarieren • Bei der Zuweisung von Werten wird aber nicht erzwungen, dass der vorgegebene Wertebereich eingehalten wird enum wochentage {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag}; int main(void) { enum wochentage w1 = Mittwoch; enum wochentage w2 = 77; /* w1 hat den Wert 2 */ /* Funktioniert auch, w2 hat den Wert 77 */ } • Kombination mit typedef, um enum bei der Variablendeklaration nicht schreiben zu müssen: typedef enum {Montag, Dienstag,...} Wochentage; int main(void) { Wochentage w1 = Mittwoch; } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 32 Werden Variablen vom Typ eines Datentypes deklariert (wie oben gemacht), so handelt es sich beim effektiven Datentyp dahinter immer um einen int. Sehr viel bringt diese Deklaration von Variablen mit dem enum-Typ nicht, denn der Compiler prüft nicht, ob dieser Variable dann auch wirklich nur Werte aus dem Bereich der Aufzählung zugewiesen werden, und entsprechend wird dieses Feature auch nur selten verwendet. Die Kombination von typedef und enum sieht man gelegentlich auch, wenn man sich einen Datentyp bool „basteln“ will (wenn man z.B. nicht direkt mit den numerischen Werten und deren Interpretation als true/false arbeiten will): typedef enum {false, true} bool /* false = 0, true = 1 */ bool flag = true /* flag = 1 */ while (flag) {...} 32 Komplexe Datentypen (3) - Strukturen • Erlaubt die Zusammenfassung von Elementen verschiedener Typen in einen Datentyp • Entspricht ~ einer Java Klasse bei welcher alle Variablen public sind und die keine Methoden enthält • Zuerst muss dieser neue Datentyp deklariert werden (meist am Anfang eines Programms): • Danach können Variablen dieses Typs deklariert/initialisiert werden: • Strukturen können auch geschachtelt werden: struct point3D { int x; int y; int z; }; struct point3D pt = {2, 4, 6}; struct line3D { struct point3D start; struct point3D end; }; Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 33 Während Java Klassen auf dem Heap abgelegt werden, sind Strukturen in C auf dem Stack abgelegt. Wenn ein int 4 Bytes beansprucht, so benötigt die Struktur im obigen Beispiel 3 * 4 = 12 Bytes Speicherplatz. 33 Komplexe Datentypen (4) - Strukturen • Der Zugriff auf einzelne Elemente (members) geschieht mit "." (wie Zugriff auf Elemente einer Java Klasse) • Wie alle Variablen können Variablen, die den Datentyp der gleichen Struktur aufweisen, einander zugewiesen werden /* point3d.c */ #include <stdio.h> struct point3D { /* Deklaration der Struktur */ int x, y, z; }; int main(void) { struct point3D ptA = {2, 4, 6}, ptB; ptB = ptA; /* Kopiert die komplette Struktur */ ptA.x = 5; /* Zugriff auf einzelne Elemente */ ptA.y += ptA.z; /* Output: A = (5,10,6), B = (2,4,6) */ printf("A = (%d,%d,%d)\n", ptA.x, ptA.y, ptA.z); printf("B = (%d,%d,%d)\n", ptB.x, ptB.y, ptB.z); } Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 34 Bei der Zuweisung werden Strukturen komplett kopiert. In diesem Sinne sind Strukturen anders als Java Klassen, wo bei der Zuweisung von Objekten nur Referenzen auf das Objekt kopiert werden, das Objekt selbst aber nicht. 34 Komplexe Datentypen (5) - Strukturen • Der Gebrauch mit struct point3D pt ist relativ mühsam, weil man das struct-Keyword immer schreiben muss • In der Praxis wird man die Struktur deshalb oft gleich auch als neuen Datentyp definiert mit typedef möglich typedef struct { Mit int x; typedef: int y; int z; } Point3D; Vgl. ohne typedef: struct point3D { int x; int y; int z; }; • Anschliessend kann man Point3D wie einen Datentypen behandeln; die Deklaration einer Variablen geschieht entsprechend wie folgt: Point3D pt =... /* statt struct point3D pt =... */ • Beachten Sie: • Name des Typs folgt mit typedef nach der Strukturdefinition • Konvention: Point3D beginnt mit einem grossen „P“ (mit typedef definierte Typen sollten immer mit einem Grossbuchstaben beginnen) Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 35 35 Zusammenfassung • Die Syntax von C hat grundsätzlich viele Ähnlichkeiten mit jener von Java • C kennt 4 grundsätzliche einfache Datentypen, wobei die Länge (Anzahl der Bytes) dieser Datentypen Systemabhängig sind • char, int, float, double • Kein boolean in C, dafür Unterscheidung signed/unsigned • Die Standard Library bietet grundlegende Input/Output Funktionen um Daten zu lesen und schreiben • C kennt typedef und struct, welche in Java nicht existieren Marc Rennhard, 24.02.2010, CPP_Programmelemente.ppt 36 36