Programmierkurs C Sommersemester 2011, 2. Vorlesung Stuttgart, den 12. April 2011 Heiko Schulz University of Stuttgart, Math. department Rückschau L Algorithmen, Berechenbarkeit, Computer, L Programmiersprachen und Compiler, L Die Programmiersprache C, Syntax, L Variablen und elementare Datentypen, L Operatoren: Zusweisungsoperator, arithmetische Operatoren . 34 Anatomie eines C-Programms I L 35 Programmkopf mit Anweisungen an Compiler/Präprozessor: #include, etc. L Blöcke von Anweisungen, Anweisungen durch Semikolon ; getrennt. L Hauptfunktion int main() mit angeschlossenem Funktionsblock in {}. L In der Hauptfunktion: return 0; zum Beenden des Programms. L Kommentare und Leerzeichen werden ignoriert. Anatomie eines C-Programms II 36 Das Programm... 1 /* */ hello .c #include 3 4 int 5 6 return 2 < s t d i o . h> main ( ) { p r i n t f (" Hallo 7 // Welt ! \ n" ) ; 0; hier ist Schluss 8 } ... ist äquivalent zu ... 1 /* hello .c Hallo */ #i n c l u d e Welt ! \ n" ) ; < s t d i o . h> return 0; / * int hier main ( ) ist { printf (" Schluss */ } Beispiel Division I / * */ div1 . c #include int int < s t d i o . h> main ( ) { a ,b,c; a= 3; b =7; c =a / b ; p r i n t f ( "% i /% i = %i \ n " , a , b , c ) ; c =b / a ; p r i n t f ( "% i /% i } return 0; = %i \ n " , b , a , c ) ; 37 Beispiel Division II / * */ div2 . c #include int float < s t d i o . h> main ( ) a= { a ,b,c; 3.0; b =7.0; c =a / b ; p r i n t f ( "%f /% f = %f \ n " , a , b , c ) ; c =b / a ; p r i n t f ( "%f /% f } return 0; = %f \ n " , b , a , c ) ; 38 Einschub: Compilieren mit make 39 Vereinfachung des Compiler-Aufrufs via make-Kommando: Wenn ein Makele existiert, wird gcc von make auf der Basis der Regeln im Makele aufgerufen. Beispiel für ein Makele: CC= g c c OPTS= all : cast : s t d =c99 W a l l cast fibonacci1 cast . c $ ( CC ) fibonacci1 : $ ( CC ) . . . ... $ (OPTS) cast . c o cast fibonacci1 . c $ (OPTS) fibonacci1 . c o fibonacci1 Einschub: Compilieren mit make 40 Bessere Variante über Platzhalter: CC= g c c OPTS= all : s t d =c99 W a l l cast inkr fibonacci1 quadrat wochentage %: fibonacci2 scanftest2 div1 div2 %. c $ ( CC ) $ (OPTS) fibonacci3 scanftest $< o $@ Dabei ist L $< die erste Abhängigkeit und L $@ der Name des zu erzeugenden Objekts. summe fibonacci4 tuwas Vergleichsoperatoren L Vergleichsoperatoren: @, A @ ,A , L ! : kleiner als, gröÿer als, : kleiner oder gleich als, gröÿer oder gleich als, : gleich, ungleich. Die Vergleichsoperatoren haben geringere Priorität als arithemtische Operatoren. L Die Operatoren <, >, <= und >= haben eine höhere Priorität als == und !=. 41 Logische Operatoren L Auswertung logischer (Bool'scher) Aussagen. L Trit ein Vergleich zu, so spricht man von TRUE. In C steht ein 42 Wert ungleich 0 für TRUE. L Trit ein Vergleich nicht zu, so spricht man von FALSE. In C steht der Wert 0 für FALSE. L Operatoren: ! steht für Negation, && für die UND-Verknüpfung (AND) und || für die ODER-Verknupfung (OR). L Die Logischen Operatoren && und || haben eine geringere Priorität als die Vergleichsoperatoren. L && besitzt eine höhere Priorität als || L Die Priorität des Negationsoperators ! ist sehr hoch, nur der Klammeroperator ( ) besitzt eine höhere Priorität. Zusammengesetze Zuweisungsoperatoren L Ein Ausdruck wie a = a + b; kann, um Schreibarbeit einzusparen, durch a += b; ersetzt werden. L In gleicher Weise ergeben sich die zusammengesetzten Operatoren -=, *=, /= und %= L Diese Schreibweise ist (insbesondere bei + und -) sehr weit verbreitet. L Beispiele: a i += = 10; 1; erhöht der Wert von a um 10 und erniedrigt den Wert von i um 1. 43 Inkrement- und Dekrement-Operatoren L In C-Programmen gibt es oft Zählvariablen, die inkrementiert (Addition von 1) oder dekrementiert (Subtraktion von 1) werden müssen. L Dafür gibt es die Operatoren ++ (Inkrementoperator) und - (Dekrementoperator). L Die Operatoren - - und ++ können sowohl links (Präx-Schreibweise) als auch rechts (Postx-Schreibweise) einer Variablen stehen. L Bei Präxoperatoren wird die In- bzw. Dekrementierung vor allen anderen Operationen durchgeführt, bei Postxoperatoren danach. L Inkrement- und Dekrement-Operatoren dürfen nicht auf der linken Seite einer Zuweisung angewendet werden. L Die Priorität ist gleich wie die des Negationsoperators (!). 44 Inkrement- und Dekrement-Operatoren Beispiel / * */ inkr . c #include int int < s t d i o . h> main ( ) a , 45 { b; a =10; a ++; // a p r i n t f ( " a=% i \ n " , b=a + + ; // p r i n t f ( " a=%i , b=++a ; // p r i n t f ( " a=%i , } return 0; b hat jetzt hat jetzt b=% i \ n " , a den Wert 11 den Wert 11 a) ; und b b=% i \ n " , a , haben a , und a den b) ; jetzt b) ; den Wert 13 Wert 12 Funktionen I L 46 Wozu gibt es Funktionen? Zur Strukturierung von Programmen, zur Wiederverwendung von Code-Teilen. L Es wird zwischen Deklaration und Denition einer Funktion unterschieden. L Eine Deklaration hat die Form TYP FUNKTIONSNAME ( TYP PARAM1 , TYP PARAM2 , ...) ; Sie deklariert den Funktionsprototyp, d.h. Name, Rückgabetyp und Anzahl der Parameter, sowie Parametertypen der Funktion. L Eine Denition hat die Form TYP FUNKTIONSNAME ( TYP PARAM1 , TYP PARAM2 , LOKALE VARIABLENDEKLARATIONEN ; ANWEISUNGEN ; } return . . . ; ...) { Funktionen II L 47 Oft: Deklaration und Denition in einem Schritt, wie bei Variablendeklaration und gleichzeitiger Zuweisung. L Soll die Funktion keine Rückgabewerte haben, so verwendet man als Rückgabetyp den leeren Datentyp void, soll sie keine Parameter haben, kann man für die Deklaration/Denition auch schreiben: TYP FUNKTIONSNAME ( void ) { ... oder TYP FUNKTIONSNAME ( ) { ... } } Funktionen Beispiele I L Hauptfunktion int main() {... return 0; } L Funktion ohne Rückgabewert und Parameter: void tuwas ( ) printf (" ich } int main ( ) tuwas ( ) ; tuwas ( ) ; } { { tue etwas . . . \ n" ) ; 48 Funktionen Beispiele II L Quadratzahl: int return quadrat ( a int *a ; a) 49 { } int int main ( ) { a ,b; a= q u a d r a t ( 3 ) ; p r i n t f ( " a=% i \ n " , a) ; b= q u a d r a t ( a ) ; p r i n t f ( " a=%i , b=% i \ n " , a , b) ; b=% i \ n " , a , b) ; b= q u a d r a t ( b ) ; p r i n t f ( " a=%i , } return 0; Funktionen Beispiele III L Summe: int int int return summe ( a , c; int b) 50 { c =a+b ; c; } int int main ( ) { a ,b,c; a =1; b =1; c =summe ( a , b ) ; c =summe ( c , 3 ) ; p r i n t f ( " a=%i , } return 0; b=%i , c=% i \ n " , a , b, c) ; Variablen und ihre Gültigkeit L Variablen besitzen einen Gültigkeitsbereich (scope), innerhalb dessen sie leben. L Man unterscheidet dabei den globalen Gültigkeitsbereich (programmweit), den Datei-Gültigkeitsbereich (dateiweit), den Funktions-Gültigkeitsbereich (funktionsweit) und den Block-Gültigkeitsbereich (innerhalb eines Blockes). L Variablennamen (und Funktionsnamen) müssen innerhalb ihres Gültigkeitsbereiches eindeutig sein. L Priorität der Gültigkeitsbereiche von global nach lokal, d.h. lokale Variablen überdecken Sichtbarkeit globaler mit gleichem Namen. Man sollte doppelte Verwendung von Variablennamen trotzdem möglichst vermeiden. 51 Variablen und ihre Gültigkeit, Beispiel L 1 2 3 4 5 Beispiel: int int int a =0; // g l o b a l e main ( ) a , Gueltigkeit { b, c; // f u n k t i o n s w e i t e Gueltigkeit a =1; 6 b =1; 7 c =4; 8 9 { 10 11 12 13 int c , d; // b l o c k w e i t e Gueltigkeit c =2; d =5; } 14 15 a =2 *c ; // a hat den Wert 8 16 b=a+b ; // b hat den Wert 9 17 a=b+d ; // F e h l e r , 18 19 } return 0; d nicht mehr definiert ! 52 Allgemeine Anatomie eines C-Programms 1 Include Befehle 2 Globale Deklarationen ( Variablen , Funktionen , 3 4 TYP FUNKTION1 ( TYP PARAMETER1 , 5 LOKALE VARIABLEN ; 6 ANWEISUNGEN ; 7 8 } return ...) { ...) { . . . ; 9 10 TYP FUNKTION2 ( TYP PARAMETER1 , 11 LOKALE VARIABLEN ; 12 ANWEISUNGEN ; 13 14 } 15 16 17 return int 18 19 20 } 53 . . . ; main ( ) { LOKALE VARIABLEN ; ANWEISUNGEN ; return . . . ; ...) Umwandeln von Datentypen (cast) 54 L Explizite Datentypumwandlungen erreicht man mithilfe von Casts . L Ein Cast ist ein geklammerter Datentyp, der dem umzuwandelten Ausdruck vorangestellt wird. L Syntax: (DATENTYP) AUSDRUCK; L Der Wert von AUSDRUCK wird in DATENTYP umgewandelt. L Beispiel: 1 2 int double double int n; d; 3 n = 5; 4 d = ( 5 d += 6 n = ) n; d; // 1.5; ( ) Vorsicht : Information geht verloren ! Kontrolluss L Algorithmen lassen sich durch Flussdiagramme (Ablaufdiagramme) beschreiben, zum Beispiel: L Kontrolluss: Verzweigungen, bedingte Ausführung, Schleifen, Sprünge. Wie lassen sich diese Instrumente in C umsetzen? 55 Die if-Anweisung bedingte Ausführung L if-Anweisungen haben die Syntax if ( bedingung ) anweisung ; bedingung ein Bool'scher Ausdruck (wahr oder falsch) sein bedingung erfüllt, wird anweisung ausgeführt. Falls anweisung ein Block ist ( { ... } ). lässt man das Semikolon nach anweisung weg. wobei muss. Ist L Soll im Alternativfall etwas anderes getan werden, wird der Befehl else verwendet: if ( bedingung ) anweisung1 ; else anweisung2 ; bedingung erfüllt, wird anweisung1 ausgeführt, ansonsten anweisung2. Dabei können anweisung1 und anweisung2 auch Blöcke sein, wobei wieder die Semikolons nach anweisung1 und anweisung2 weggelassen werden. Ist 56 Die if-Anweisung bedingte Ausführung L Noch genauere Verzweigung: if ( bedingung1 anweisung2 ; ) ... anweisung1 ; else else i f 57 ( bedingung2 ) anweisungn ; wobei die Anweisungen wieder auch Blöcke sein können. L Merke für Bedingungen: 0 bedeutet falsch, alle anderen Ganzzahl-Werte bedeuten wahr. L Vorsicht: = ist der einfache Zuweisungsoperator, == ist der Vergleichsoperator, der auf Gleichheit testet, eine häuge Fehlerquelle. Die if-Anweisung bedingte Ausführung L 1 2 3 Beispiel: int if 4 } 5 6 } 7 8 9 } groesser ( ( a >b ) { return else i f return else return a , 1; ( a <b ) 1; { 0; } int { int b) { 58 Die if-Anweisung Fallstricke L 1 2 3 FALSCH: int if 4 } 5 6 } 7 8 9 } groesser ( ( a=b ) { return else i f return else return a , 0; 1; { int b) Zuweisung ! ! ( a <b ) 1; } int // { { 59 Die switch-Anweisung L 60 Bestehen die Alternativen bei einer if-else-Abfrage aus ganzen Zahlen oder einzelnen Zeichen, kann man stattdessen die switch-Anweisung verwenden. So ist... if ( ausdr == ) == ausdr0 ausdr1 { ... ) } { ) { aktion1 ; else { aktion0 ; } } else i f standardaktion ; ... äquivalent zu... switch case case case default ( } ausdr ) { ausdr0 : aktion0 ; ausdr1 : aktion1 ; ausdr2 : ... : break break standardaktion ; ; ; else i f ( ausdr } ( ausdr == ausdr2 Die switch-Anweisung L case gefolgt von ausdr und : ist eine Sprungmarke und wird nicht als Anweisung interpretiert. Sprungmarken (auch vom Muster Name:) werden bei der Ausführung ignoriert. L Der Befehl break; führt zum Verlassen des switch-Blocks. L Ohne break geht es weiter zum nächsten Fall. Anwendung: Initialisierung in mehreren Stufen. Aber vergessene breaks sind auch beliebte Fehlerquelle! 61 Die switch-Anweisung Beispielprogramm L Beispiel: int wochentag ; wochentag =3; switch case break case break case break ( wochentag ) { 0: p r i n t f ( " Noch 5 Tage 1 Tag bis zum Wochenende . \ n " ) ; ; ... 4: p r i n t f ( " Noch bis zum Wochenende . \ n " ) ; ; 5: p r i n t f ( " Juhu , } ; es ist Wochenende ! \ n " ) ; 62 Die bedingte Auswertung 63 bedingung ? ausdr1 : ausdr2 L Ist bedingung wahr, so wird ausdr1 ausgewertet, andernfalls ausdr2. Es handelt sich also um eine verkürzte if-Anweisung. L Syntax: L Beispiel: Anstatt if else ( zahl1 < zahl2 ) min = zahl1 ; min = zahl2 ; zu schreiben, kann man auch kürzer min = ( schreiben. zahl1 < zahl2 ) ? zahl1 : zahl2 ; Die for-Schleife L 64 Syntax: for ( wobei ausdruck1 ; anweisung bedingung ; ausdruck2 ) anweisung ; auch ein Anweisungsblock sein kann, der mit {. . . } geklammert werden muss. ausdruck1 initialisiert die Schleifenvariable(n). L bedingung ist die Abbruchbedingung der Schleife. Wenn bedingung falsch ist, wird die for-Schleife beendet. L ausdruck2 reinitialisiert die Schleifenvariable(n) (beispielsweise L durch Inkrementierung). L Vorsicht: Kein Semikolon am Ende des for-Schleifenkopfs, wie in for int ( L i =0; i < n; i ++) ; p r i n t f ( "\%d \ n " , i ) ; Seit C99 dürfen Zählvariablen im Schleifenkopf deklariert werden. Die for-Schleife Beispielprogramm / * */ fibonacci1 . c #include int int main < s t d i o . h> () { aktuelle , n = vorletzte , = i , n; 0; letzte = printf ( " a_0 = %d \ n " , vorletzte printf ( " a_1 = %d \ n " , letzte ( i = 1; 2; i aktuelle printf letzte } return 0; <= = n; = = i letzte ( "a_%d vorletzte } letzte , 10; vorletzte for 65 ++) + = %d letzte ; aktuelle ; ) ; ) ; { vorletzte ; \n" , i , aktuelle ) ; Die while-Schleife I L 66 Syntax: while wobei ( bedingung anweisung ) anweisung ; auch ein Anweisungsblock sein kann, der mit {. . . } geklammert werden muss. L anweisung ausgeführt wird, wird bedingung geprüft. bedingung bereits bei der ersten Auswertung falsch, wird Bevor Ist die Schleife nie durchlaufen. L Jede for-Schleife lässt sich durch eine äquivalente while-Schleife ersetzen. Die while-Schleife II / * fibonacci2 . c #include int int () { aktuelle , // letzte , vorletzte , 10; vorletzte = 0; letzte = printf ( " a_0 = %d \ n " , vorletzte printf ( " a_1 = %d \ n " , letzte i i , n; Initialisierung = = 1; ) ; ) ; 2; while ( i <= aktuelle printf letzte i ++; } return 0; n = ) = = { letzte ( "a_%d vorletzte } */ < s t d i o . h> main n 67 + vorletzte ; = %d \ n " , letzte ; aktuelle ; i , aktuelle ) ; Die do . . . while-Schleife I L L Syntax: do anweisung while ( 68 bedingung ) ; Im Gegensatz zu for- und while-Schleifen wird bedingung immer am Ende eines Schleifendurchlaufs geprüft. Daher wird die Schleife immer mindestens einmal durchlaufen. bedingung L Ist L Wegen der besseren Lesbarkeit eines C-Programms sollte das abschlieÿende Zeile stehen. wahr, wird die Schleife ein weiteres Mal durchlaufen. } while (bedingung); immer auf einer extra Die do . . . while-Schleife II / * fibonacci3 . c #include int int main = */ < s t d i o . h> () { aktuelle , n 10; letzte , vorletzte = vorletzte , 0; letzte = printf ( " a_0 = %d \ n " , vorletzte printf ( " a_1 = %d \ n " , letzte i do = n; ) ; ) ; 2; aktuelle = letzte ( "a_%d vorletzte letzte = = while return ( i letzte ; aktuelle ; 0; <= + = %d i ++; } i , 1; { printf } 69 n ) ; vorletzte ; \n" , i , aktuelle ) ; Die Anweisungen break und continue I L ...bieten zusätzliche, a-priori nicht bekannte Abbruchbedingungen für Schleifen. L break bricht den Schleifendurchlauf ab und springt aus der Schleife heraus. L continue bricht den aktuellen Schleifendurchlauf ab und setzt mit dem nächsten Durchlauf fort. L Ziel: Vermeidung von Unübersichtlichkeit durch viele if-Blöcke. 70 Die Anweisungen break und continue II L 71 Beispielprogramm Nur diejenigen der ersten n Fibonacci-Zahlen sollen ausgegeben werden, die kleiner als 1000 und durch 3 teilbar sind. / * main n, n = for < s t d i o . h> () { aktuelle , if if letzte , vorletzte , i ; 5000; ( letzte vorletzte aktuelle = = = vorletzte letzte , letzte + ( aktuelle >= ( aktuelle % 3) printf } } */ fibonacci4 . c #include int int return 0; ( "a_%d \t = 1 , letzte i = = 2;; i aktuelle = i ) { vorletzte ; break continue 1000) = %d ; ; \n" , i , aktuelle ) ; + 1, Die goto-Anweisung I 72 L Sprung an eine vordenierte Sprungmarke (Label). L Einzig sinnvolle Anwendung: Abbruch mehrfach geschachtelter Schleifen. Vorsicht: schlechter Stil. L 1 2 3 4 5 6 Beispiel: while for if { ( =0; i i < sehrgross ; i ++) { ... ( irgendwas 8 ( i )) { < auchnichtklein ; ... for if 7 ( j =0; j ... 9 10 ( bedingung ( i , j ) } 11 12 (1) ... } } 13 } 14 S c h l e i f e n e n d e : ... ) { goto j ++) { Schleifenende ; } Die goto-Anweisung II L Andere Möglichkeit: 1 bool 2 3 4 5 6 7 73 Abbruch while for if = i false ; 0; i < sehrgross ; i ++) { ( irgendwas ( i )) { ... for if ( j = 0; j < auchnichtklein ; j ++) { ... 10 11 } if 12 15 } ( = ... 9 14 { ... 8 13 (1) if } ( ( ( bedingung break ; break break Abbruch Abbruch ) ( i , } ) ; ; j ) ) { Abbruch = true ; Dateneingabe Die Funktion scanf L Die Funktion scanf dient zum Einlesen von Benutzereingaben. L Syntax: scanf (" format " , Der Formatstring printf. L &v a r 1 format ,...) ; ist nahezu gleich aufgebaut wie der von Zu beachten: Nicht die Variable wird an scanf übergeben, sondern eine Referenz (&) auf die Variable. Dazu später mehr! L Nicht-Formatanweisungen im Formatstring müssen mit eingegeben werden, z.B. bei... scanf ( " g a n z=%d " , &g a n z ) ; müsste ganz= mit eingegeben werden. 74 Die Funktion scanf Beispielprogramm / * scanftest . c #include int int char float main 75 */ < s t d i o . h> () { ganz ; zeichen ; printf scanf printf scanf printf scanf gleit ; (" Bitte ( "% i " , (" Bitte ( "%s " , p r i n t f ( " ganz zeichen , } return 0; sie eine sie ein ganze Zahl ein : ") ; geben Zeichen ein : ") ; &z e i c h e n ) ; (" Bitte ( "%f " , geben &g a n z ) ; geben sie eine Gleitkommazahl ein : ") ; &g l e i t ) ; = %d \ n z e i c h e n gleit ) ; = %c \ n g l e i t = %f \ n " , ganz , Vielen Dank... ... für Ihre Aufmerksamkeit und bis morgen... 76