Grundlagen der Programmierung Friedrich-Schiller-Universität Jena Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 1 1 Einleitung Was sind Programme? Einfache und schnelle Lösung mathematischen Problemen Warum programmieren? Erarbeitung eines Algorithmus Erstellung einer Abarbeitungsvorschrift (Programm) für den Computer in einer speziellen Sprache Übersetzen des Algorithmus in die Maschinensprache Abarbeitung des Programms im Computer Ergebnis Friedrich-Schiller-Universität Jena Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 2 2 Klassifikation nach Sprachtypen C# 2000 Visual Basic Delphi Java 1990 Ada 1980 C++ C Pascal 1970 Algol 68 Basic Friedrich-Schiller-Universität Jena 1960 Cobol Algol 60 Fortran Universitätsrechenzentrum Multimediazentrum Prozedurale Sprachen 1950 Objektorientierte Sprachen C-Programmierung 2003/2004 3 3 Funktionale Sprachen: - Lisp (1960) - das Programm besteht aus Funktionen, die sich gegenseitig aufrufen - Programme werden wie mathematische Funktionen geschrieben - Funktion hat einen Definitions- und Wertebereich - Funktion erhält Eingebewert und berechnet den Wert der Funktion - Einsatz im Forschungsbereich für künstliche Intelligenz und im Bereich der Mathematik Logische Sprachen: - Prolog (1970) - Sprachen sind auf die Lösung von bestimmten Friedrich-Schiller-Universität Jena Problemen abgestimmt (Datenbankabfragen) - das Programm bestimmt eine logische Beziehung zwischen den einzelnen Daten Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 4 4 Klassifikation nach Sprachgenerationen Erste Generation: Maschinensprache - Programmierung auf Maschinenniveau (0110101010) Zweite Generation: Assembler-Sprache - Programmierung auf Prozessorniveau; Anwendung von speziellen Befehlen (LD 3,4 ; ADD ww1,dd4) Dritte Generation: Problemorientierte Sprachen - PL/1; Pascal, Cobol, Ada, C/C++, Basic usw. Vierte Generation: Deklarative Sprachen - SQL, Open Access Friedrich-Schiller-Universität Jena Fünfte Generation: Künstliche Intelligenz - Lisp, Prolog, Smalltalk Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 5 5 Entwicklung und Ausführung eines Programms Problem Lösung mittels eines Algorithmus Modul 1 Modul 2 Compiliertes Modul 1 Compiler Modul 3 Friedrich-Schiller-Universität Jena Ausführbares Programm mit absoluten Adressen mehrere Module Compiliertes Modul 2 Compiliertes Modul 3 Lader Fertiges Programm mit relativen Adressen (Lademodul) Linker (Verbinder) Interpreter Programm Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 6 6 Entwicklungsumgebung Compiler und Linker sind für unterschiedliche Betriebssystemplattformen verfügbar Für viele Betriebssysteme gibt es mächtige Entwicklungswerkzeuge, sogenannte Entwicklungsumgebungen. In diesen Tools sind die: • Projektverwaltung, • Editoren, •Grafiktools, •Compiler, •Debugger und •Linker Friedrich-Schiller-Universität Jena unter einer gemeinsamen Oberfläche vereinigt. Der Entwickler kann damit von der Quellcoderfassung bis zum fertigen Tool alles mit einem Werkzeug realisieren. Damit ist eine sehr effektive Arbeit möglich Weiterhin gehören zu jeder Programmiersprache noch entsprechende Standardbibliotheken. Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 7 7 Microsoft Visual C++ 6.0 Friedrich-Schiller-Universität Jena Universitätsrechenzentrum Multimediazentrum C-Programmierung 2003/2004 8 8 Programmieren in C C-Programmierung 2003/2004 9 9 Inhaltsverzeichnis 1. 2. 3. 4. 5. 6. 7. 8. 8.1. 8.2. 8.3. 8.4. 8.5. 9. 10. 10.1. 10.2. 10.3. Grundlagen Funktionen Variablentyp int Eingabe mit scanf Grundrechenarten Entscheidung if Gültigkeitsbereich von Variablen Funktionen mir Parameterübergabe Parameterübergabe Wertrückgabe Übergabe mehrerer Parameter Prototypen parametrisierter Funktionen spezielle Hinweise zu Funktionen mit Parameterübergabe Die for-Schleife Schleifen mit Ein- und Austrittsbedingungen Schleifen mit Eintrittsbedingung Schleifen mit Austrittsbedingung Continue-Anweisung C-Programmierung 2003/2004 10 10 11. 12. 13. 14. 15. 15.1. 15.2. 15.3. 16. 17. 18. 19. 20. 21. 21.1. 21.2. 21.3. 21.4 21.5 21.6 22. Logische Operatoren Variablen vom Typ float Fallunterscheidungen Der Variablentyp char Zeiger und Vektoren Adressen als Funktionsparameter Zeiger auf Funktionen Zeiger auf Zeiger Variablenfelder Zeichenketten - String Strukturen Arbeit mit Dateien Präprozessor-Befehle Programmier-Technologien Eigene Header Dateien Rekursion Listen Sortierverfahren Suchalgorithmen Datenkomprimierung Zusammenfassung C-Programmierung 2003/2004 11 11 1. Grundlagen Das erste C-Programm: void main(void) /*Ein C-Programm*/ { } - dieses Programm realisiert nichts - jedes C-Programm beginnt immer mit main (Haupt; Hauptfunktion) - vor dem Funktionsaufruf steht void, das bedeutet, dass die Funktion keinen Rückgabewert liefert - das void nach dem Funktionsaufruf bedeutet, dass dem Funktionsaufruf keine Werte übergeben werden - hinter dem Funktionsaufruf stehen geschweifte Klammern, welche die Anweisungen der Funktion umschließen (dürfen nie fehlen !) void=leer C-Programmierung 2003/2004 12 12 - geschweifte Klammern fassen Anweisungen zu einem Block zusammen - Zusammengehörige geschweifte Klammern sollten immer untereinander stehen - Kommentare werden immer in /* Kommentar */ eingeschlossen und dürfen nicht verschachtelt werden Ausgabe mit printf - eine Grundfunktion mit der Ausgaben aller Art unterstützt werden heißt printf( ) #include <stdio.h> void main(void) { printf("Das ist eine Bildschirmausgabe"); } - der Funktion printf wird eine Stringkonstante übergeben, die während der Ausführungszeit nicht verändert wird C-Programmierung 2003/2004 13 13 - Stringkonstanten stehen immer innerhalb von doppelten Anführungszeichen -Anweisungen, denen kein Anweisungsblock folgt, werden mit einem Semikolon abgeschlossen - #include <stdio.h> Standardbibliothek stdio.h an das C-Progamm binden - der #include-Befehl ist kein Bestandteil der Sprache C, sondern der Befehl eines Präprozesses - Präprozesse werden nur vom Compiler benötigt und nehmen bestimmte Änderungen während der Compilierung im Programm vor - ohne #include kommt es zu Fehlermeldungen während der Compilierung, da der Compiler die Funktion printf nicht kennt - Präprozessorbefehle erkennt man am # in der ersten Spalte - diese Befehle dürfen nicht nach rechts eingerückt werden !!! C-Programmierung 2003/2004 14 14 Steuerzeichen für die Ausgabe #include <stdio.h> void main(void) { printf ("Ausgabetext-1"); printf ("Ausgabe-2"); } Ausgabe am Bildschirm: Ausgabetext-1Ausgabe-2 Durch den Einsatz von Steuerzeichen kann die Bildschirmausgabe den Erfordernissen des Nutzers angepasst werden. C-Programmierung 2003/2004 15 15 Esc-Sequenz Aktion \t HAT (horizontal tab), der Cursor geht zur nächsten horizontalen Tabulatorposition \v VT (vertical tab), der Cursor geht zur nächsten vertikalen Tabulatorposition \" " wird ausgegeben \' ' wird ausgegebeb \? ? wird ausgegeben \\ \ wird ausgegeben Wenn C keine Steuerinformation erhält, dann wird auch bei der Aus- und Eingabe keine Aktivität ausgelöst!!! #include <stdio.h> Programm01 void main(void) { Ausgabe am Bildschirm: printf ("Ausgabe-1\n"); Ausgabe-1 printf ("Ausgabe-2"); Ausgabe-2 } C-Programmierung 2003/2004 16 16 Alternativ (Ausgabe in einer printf-Anweisung) #include <stdio.h> vioid main(void) { printf ("Ausgabe-1\nAusgabe-2"); } Esc-Sequenz Aktion \a BEL (bell) gibt akustisches Signal \b BS (backspace), Curser geht eine Position nach links \f FF (formfeed), Seitenvorschub wird ausgelöst \n NL (new line), Curser geht zum Anfang der nächsten Zeile \r CR (carriage return), der Cursor geht zum Anfang der aktuellen Zeile C-Programmierung 2003/2004 17 17 2. Funktionen Die Zerlegung eines Programms in verschiedene Funktionen dient der Verbesserung der Übersichtlichkeit und der effektiveren Realisierung des Programmablaufs. Programme sollten immer in sinnvolle Blöcke gegliedert werden!!! Diese Planung sollte bereits vor der Programmierung erfolgen, denn später lässt sich eine Gliederung in mehrere Blöcke (Funktionen) meist nicht mehr realisieren. #include <stdio.h> void funktion1(void) { printf ("Ausgabe-1\n"); } void funktion2(void) { printf ("Ausgabe-2\n"); } C-Programmierung 2003/2004 18 18 void main(void) { funktion1( ); funktion2( ); } funktion1 funktion2 { printf ("Ausgabe-1\n"); } { printf ("Ausgabe-2\n"); } Start main funktion1 funktion2 Ende Funktionsaufruf C-Programmierung 2003/2004 main 19 19 Funktionsnamen müssen mit Buchstaben oder Unterstrich beginnen und dürfen nur Buchstaben, Ziffern oder Unter- striche enthalten. Bei Namen wird auf Groß- und Kleinbuchstaben geachtet. Nur die ersten 31 Zeichen werden bei der Namensunterscheidung benutzt. Diese Regeln gelten für alle Namen (Variablen, Funktionsnamen, Strukturnamen usw). C-Programmierung 2003/2004 20 20 Funktionsdeklarationen und Prototypen #include <stdio.h> void funktion1(void); void funktion2(void); Prototyp void main(void) { funktion1(); funktion2(); } void funktion1(void) { printf("Ausgabe-Text-Funktion1\n"); } void funktion2(void) { printf("Ausgabe-Text-Funktion2\n"); } C-Programmierung 2003/2004 Funktionsdeklaration 21 21 3. Variablentyp int Variablen bestehen aus Buchstaben oder einem Wort (evtl. in Verbindung mit Ziffern) und repräsentieren einen Wert. Variablentyp int (integer) = ganzzahlig z.B. 2, 99, -23, 387 usw. Varianten von Integer-Variablen Typ Größe Zahlenbereich FZ int 2 Byte -32768 bis +32767 i unsignetd int 2 Byte 0 bis 65535 u long 4 Byte - 2147483648 bis + 2147483647 li unsigned long 4 Byte 0 bis 4294967295 lu short - 128 bis + 127 hi 0 bis 255 hu 1 Byte unsigned short 1 Byte FZ = Formatzeichen C-Programmierung 2003/2004 22 22 Die angegebenen Längen von Integer Variablen sind Mindestgrößen, die ein Compiler benutzen muss, es können aber auch mehr sein. C-Programmierung 2003/2004 23 23 Formatzeichen für die Ausgabe Um printf innerhalb eines Strings mitzuteilen welcher Variablentyp zu benutzen ist, muss das entsprechende Formatzeichen angegeben werden. printf ("Ausgabe einer Int Zahl mit 2 Byte: %i", zahl1); an %i kann man erkennen, daß Zahl1 eine Variable vom Typ int sein muss !!! printf ("Ausgabe von Zahlen %i, %hu, %li", x1, x2, x3); int unsigned int long int C-Programmierung 2003/2004 24 24 #include <stdio.h> void main(void) { int zahl1; int zahl2; int zahl3; zahl1=19; zahl2=-23; zahl3=299; printf("Die Zahlen lauten %i, %i und %i\n",zahl1,zahl2, zahl3); } Bildschirmausgabe: Die Zahlen lauten 19, -23 und 299 C-Programmierung 2003/2004 25 25 1. Variablen werden definiert 2. Variablen wird ein Wert zugewiesen = initialisieren Dies erfolgt in C durch den Zuweisungsoperator "=" 3. Steht rechts vom Gleichheitszeichen ein Ausdruck, z.B. a=b+c, wird zuerst berechnet und dann der Wert zugewiesen. Vor dem Gleichheitszeichen dürfen keine konstanten Ausdrücke stehen verkürzte Schreibweise int zahl1; int zahl2; int zahlxyz; int a; kann auch wie folgt realisiert werden: int zahl1, zahl2, zahlxyz, a; oder sofort mit Wertzuweisung: int zahl1=222, zahl2=333, zahlxyz=-234, a=-99; C-Programmierung 2003/2004 26 26 Variablen, welche nicht initialisiert sind, haben immer einen undefinierten Wert. Variablen prinzipiell initialisieren Erweiterte Formatierung bei der Ausgabe printf ("Ausgabe des Wertes %+i", 99); Der Wert bekommt immer ein Vorzeichen +99 printf ("Ausgabe des Wertes %5i und %5i\n", 99, 33333); Beide Zahlen haben die gleiche Länge (hier 5). Fehlende Stellen werden mit Leerzeichen aufgefüllt. Ist die Zahl länger als der reservierte Platz, wird sie trotzdem komplett ausgegeben C-Programmierung 2003/2004 27 27 printf ("Ausgabe des Wertes %-5i und %-5i\n", 99, 33333); Werte linksbündig ausgeben printf ("Ausgabe des Wertes %05i und %05i\n", 99, 33333); Mit führenden Nullen auffüllen C-Programmierung 2003/2004 28 28 4. Eingabe mit scanf Eingabe erfolgt mit der Funktion scanf. Diese Funktion steht genau wie printf in der Headerdatei stdio.h. #include <stdio.h> void main(void) { unsigned int zahl; printf("Geben Sie bitte eine Zahl größer gleich Null und kle\ iner 65536 ein:"); scanf("%u",&zahl); printf("Sie haben %u eingegeben.\n",zahl); } Trennsymbol "\" Folgezeile nicht einrücken C-Programmierung 2003/2004 29 29 scanf("%u",&zahl); Formatzeichen Adresse von Zahl (&=Addreßoperator scanf("Bitte eine Zahl eingeben %u", &zahl); Text wird ignoriert !!!!! printf ("Bitte eine Zahl eingeben:"); scanf ("%i",&zahl); Richtig !!!!! C-Programmierung 2003/2004 30 30 Achtung: Die Programmiersprache C prüft bei scanf nicht die Überschreitung des Wertebereiches der definierten Variablen !!!!! Programmierer muss selbst die Richtigkeit der Eingaben prüfen. Einfügen von entsprechenden Prüfprogrammen (Funktionen). C-Programmierung 2003/2004 31 31 5. Grundrechenarten + Addiert zwei Werte - Subtrahiert den rechten Wert vom linken * Multipliziert zwei Werte miteinander / Dividiert den linken Wert durch den rechten Punktrechnungen (* und /) binden stärker als Strichrechnungen ( + und -). Wird eine andere Reihenfolge der Berechnung benötigt, so müssen Klammern verwendet werden. aa=5*3+zahl1; aa=5*(3+zahl1); int-Variablen nehmen nur ganzzahlige Werte auf. Bei "/" werden die entstehenden Nachkommastellen weggelassen (es erfolgt keine Rundung). C-Programmierung 2003/2004 32 32 Der Modulo-Operator % Der Modulo-Operator bestimmt den Rest einer Division von Ganzzahlen. 13%4 = 1 denn 13/4=3 und 3*4=12 13-12=1 23%5 = 3 denn 23/5=4 und 5*4=20 23-20=3 Anwendungsfall: Schubladenprinzip: - es existieren 8 Schubladen 0 bis 7 - es werden Zahlen in die Schubladen einsortiert - 0 in Schublade 0 - 7 in Schublade 7 - 8 in Schublade 0 usw. 33 / 8 = 4 4 * 8 = 32 Rest 1 schublade=zahl%8; Æ Schublade 1 C-Programmierung 2003/2004 33 33 Weitere Operatoren (Zuweisungsoperatoren) += zahl+=5; zahl=zahl+5; -= zahl-=5; zahl=zahl-5; *= zahl*=5; zahl=zahl*5; /= zahl/=wert; zahl=zahl/wert; %= zahl%=6; zahl=zahl%6; Frage ? zahl=wert1/wert2 + zahl; zahl+=wert1/wert2; Die beiden Ausdrücke sind nur deshalb äquivalent, da "/" eine stärkere Bindung hat als "+". C-Programmierung 2003/2004 34 34 zahl=zahl*wert1+wert2 ; zahl*=wert1+wert2; falsch Achtung: Es erfolgt immer erst die Berechnung rechts und dann die Zuweisung !!!!! C-Programmierung 2003/2004 35 35 Ausgabe von Ausdrücken An jeder Stelle im Programm, an der eine Variable stehen kann, ist auch ein berechenbarer Ausdruck möglich. Außer auf der linken Seite eines Ausdruckes. z3=z1+z2; printf ("%i plus %i gleich %i\n", z1,z2,z3); oder printf ("%i plus %i gleich %i\n", z1, z2, z1+z2); oder printf ("%i plus %i gleich %i\n", z1, z2, z3=z1+z2); Steht an einer Stelle im Programm ein berechenbarer Ausdruck, kann er auch dort zugewiesen werden. C-Programmierung 2003/2004 36 36 An jeder Stelle im Programm, an der eine Variable steht, kann auch eine Konstante stehen. printf ("Die Zahl heißt %i\n" , 123); oder printf ("Die Zahl heißt 123"); C-Programmierung 2003/2004 37 37 Die Operatoren ++ und -Eine Variable wird inkrementiert (++), wenn ich ihren Wert um eins erhöhe. Eine Variable wird dekrementiert (--), wenn ich ihren Wert um eins erniedrige. Verwendung: Bei der Benutzung von Zählern !!! wert=3; printf ("Die Zahl lautet %i", wert); wert++; printf ("Die Zahl lautet %i"; wert); wert--; printf ("Die Zahl lautet %i", wert); Als Ausgabe erfolgt in diesem Beispiel: 3 4 3 Die Operatoren stehen hinter der Variablen, d.h. das de- bzw. inkrementieren wird nach der Benutzung der Variablen ausgeführt. Dieser Vorgang wird als Postinkrement bzw. Postdekrement bezeichnet. C-Programmierung 2003/2004 38 38 wert=3; printf ("Die Zahl lautet %i", wert); ++wert; printf ("Die Zahl lautet %i"; wert); --wert; printf ("Die Zahl lautet %i", wert); Als Ausgabe erfolgt in diesem Fall 3 4 3 In diesem vorliegendem Beispiel erfolgt die Inkrementierung bzw. Dekrementierung vor der Benutzung der Variablen. Dieser Vorgang wird als Präinkrement bzw. Prädekrement bezeichnet. Warum erfolgt die gleiche Ausgabe, obwohl im ersten Beispiel die Operatoren als Postinkrement/-dekrement und im zweiten Beispiel als Präinkrement/dekrement angewendet werden ????? C-Programmierung 2003/2004 39 39 Weil in beiden Programmen die Variablen während dem Inkrementieren / Dekrementieren nicht benutzt wurden. Der Vorgang wurde separat ausgeführt C-Programmierung 2003/2004 40 40 wert=3; printf ("Das ist Zahl %i",wert++); printf ("Das ist Zahl %i",wert--); printf ("Das ist Zahl %i", wert); Das Ergebnis lautet: 3 4 3 wert=3; printf ("Das ist Zahl %i",++wert); printf ("Das ist Zahl %i",--wert); printf ("Das ist Zahl %i", wert); Das Ergebnis lautet: 4 3 3 C-Programmierung 2003/2004 41 41 Programm 02 Programm 02_2 Fragen: 1.) Ist x+=2 gleich x++2 ??? 2.) Worin liegt der Vorteil von ++ gegenüber von +=1 ??? C-Programmierung 2003/2004 42 42 6. Entscheidungen Entscheidungen werden benutzt, um den sequentiellen Programmablauf zu verändern. if (bedingung) { } if zahl==0 { printf (Achtung Wert nicht gueltig !!!!" \n); } erg=zahl01*zahl02; Abfrage auf Gleichheit erfolgt mit: == C-Programmierung 2003/2004 43 43 Der Operator ! In C hat man die Möglichkeit die Aussage einer Bedingung zu negieren, wenn "!" vor die Aussage gesetzt wird. if (! (wert==100)) { printf ("Der Wert ist ungleich 100 !!!!!"); } Besteht der Anweisungsblock hinter einer if Kontrollstruktur nur aus einer Anweisung, so können die geschweiften Klammern weggelassen werden. C-Programmierung 2003/2004 44 44 Die else-Anweisung Die else Anweisung ist ein Zusatz zu if und kann auch nur im Zusammenhang damit verwendet werden. if (a>b) { printf ("Die Zahl a ist grösser als die Zahl b"); a=a-b; } else { print ("Die Zahl a ist nicht grösser als b"); a=b-a; } Der else Zweig wird immer dann ausgeführt, wenn die Bedingung nicht erfüllt ist. Ist die Bedingung erfüllt, so wird der gesamte else Zweig ignoriert und es werden nur die Anweisungen (Anweisungsblock) hinter der Bedingung abgearbeitet. C-Programmierung 2003/2004 45 45 Vergleichsoperatoren a<b Wahr, wenn a kleiner b a<=b Wahr, wenn a kleiner gleich b a>b Wahr, wenn a größer b a>=b Wahr, wenn a größer gleich b a==b Wahr, wenn a gleich b a!=b Wahr, wenn a ungleich b Achtung: Unzulässig sind die Operatoren => und =< C-Programmierung 2003/2004 46 46 Testfragen: 1.) Der if-Anweisungsblock wird nur dann ausgeführt, wenn die Bedingung erfüllt ist? Ja/Nein???? 2.) Worin liegt der Unterschied von = und == ? 3.) Worin liegt der Unterschied in den beiden Vergleichen? (a>b) (!(a<=b)) C-Programmierung 2003/2004 47 47 #include <stdio.h> void main(void) { int zahl; printf("Bitte geben Sie eine Zahl ein :"); scanf("%i",&zahl); if(zahl<100) { printf("Die Zahl ist kleiner als Hundert.\n"); } if(zahl==100) { printf("Hundert war die gesuchte Zahl.\n"); } if(zahl>100) printf("Die Zahl ist größer als Hundert.\n"); } Programm 3.cpp C-Programmierung 2003/2004 48 48 Behandlung der Bedingung C behandelt Bedingungen wie einen Wert. Eine Entscheidung kann nur zwei Zustände annehmen, wahr oder falsch. Um die Werte für eine Entscheidung zu erhalten, kann man das folgende Programm nutzen. #include <stdio.h> void main(void) { int wahr,falsch; Programm 04 wahr=(3==3); falsch=(2==4); printf("Der Wert für wahr ist %i\n",wahr); printf("Der Wert für falsch ist %i\n",falsch); } Eine Bedingung ist falsch, wenn sie den Wert 0 hat und wahr, wenn sie einen Wert ungleich 0 hat. C-Programmierung 2003/2004 49 49 Eine Bedingung darf auch in einem berechenbaren Ausdruck stehen. Dabei nimmt sie die Werte 0 und 1 an, 0 bei falsch und 1 bei wahr. Eine Bedingung ist falsch, wenn sie den Wert 0 hat. Abfrage auf ungleich Null kann wie folgt realisiert werden: if (x) { } Der Anweisungsblock hinter if wird dann ausgeführt, wenn x ungleich null ist. Das ergibt sich daraus, dass alle Werte ungleich Null für C "wahr" sind. C-Programmierung 2003/2004 50 50 Die Abfrage auf gleich Null kann damit durch folgende Anweisung realisiert werden. if (!x) Programm 6.cpp { } Das man mit Bedingungen rechnen kann, das wurde im Beispielprogramm bereits bewiesen. C-Programmierung 2003/2004 51 51 7. Gültigkeitsbereiche von Variablen Lokale Variablen Eine Variable, die innerhalb eines Anweisungsblocks definiert ist, hat nur lokal in diesem Block ihre Gültigkeit. #include <stdio.h> Fehler beim compilieren, Variable x ist nicht definiert void aendern(void) { x=10; } void main(void) {int x=4; printf("x = %i\n",x); aendern(); printf("x = %i\n",x); } C-Programmierung 2003/2004 52 52 Wird ein Anweisungsblock verlassen, werden alle Variablen, die in ihm definiert wurden, gelöscht. Gibt es mehrere Variablen mit gleichem Namen, so spricht man immer die lokalste Variable an. Kann man Variablen auch noch lokaler als im Beispiel auf der vorherigen Seite definieren? C-Programmierung 2003/2004 ? 53 53 #include <stdio.h> void main(void) { int wert; printf("Bitte Zahl eingeben :"); scanf("%i",&wert); if(wert<=20) { int quadrat; Variable quadrat ist innerhalb des if-Blocks definiert und nur dort gültig quadrat=wert*wert; printf("Das Qudrat von %i ist %i\n",wert,quadrat); } printf("Quadrat hat den Wert %i",quadrat); } Fehler beim compilieren, da Variable quadrat nicht definiert ist !!!! Lokale Variablendefinitionen müssen immer die ersten Anweisungen des Blockes sein, in dem sie stehen !!! C-Programmierung 2003/2004 54 54 Globale Variablen #include <stdio.h> Die Variable x ist als globale Variable definiert int x=4; void aendern(void) { x=10; } void main(void) { printf("x = %i\n",x); aendern(); printf("x = %i\n",x); } Variablen, die außerhalb einer Funktion definiert werden, sind global und können somit in jeder Funktion benutzt werden. C-Programmierung 2003/2004 55 55 Globale und lokale Variablen können in einem Programm zusammen genutzt werden. Dabei sind gleiche Namen durchaus erlaubt. #include <stdio.h> Variable a und b global definiert int a,b; void aendern(void) { int a; a=0; if(a==0) Variable a lokal definiert { int a=20; printf("a=%i\n",a); } printf("a=%i\n",a); } void main(void) { a=b=10; printf("a=%i und b=%i\n",a,b); aendern(); printf("a=%i und b=%i\n",a,b); } C-Programmierung 2003/2004 56 56 Wenn eine lokale Variable definiert wurde, dann ist es nicht möglich auf globale Variablen gleichen Namens zuzugreifen. Dies ist erst in C++ möglich. Achtung void main(void) { int a; int a; Es dürfen nicht zwei Variablen mit gleichem Namen und dem gleichen Gültigkeitsbereich definiert werden a=10; } Bei der Definition von Variablen gilt folgender Grundsatz So lokal wie möglich, so global wie nötig. Globale Variablen erleichtern das Programmieren ungemein, da man in jeder Funktion auf alle Variablen ungehindert zugreifen kann. Für eine modulare Programmierung sollten globale Variablen jedoch vermieden werden. Sie sollten nur dann verwendet werden, wenn es keine andere Möglichkeit in der Programmrealisierung gibt. C-Programmierung 2003/2004 57 57 Statische Variablen Statische Variablen sind eine Mischung aus globalen und lokalen Variablen. Sie werden beim Verlassen ihres Gültigkeitsbereiches nicht gelöscht, sondern behalten ihren Wert. #include <stdio.h> void test(void) { int a=1; printf("a=%i\n",a); a++; } void main(void) { test(); test(); test(); } Was wird in diesem Programm ausgegeben ? 111 C-Programmierung 2003/2004 ? 58 58 #include <stdio.h> void test(void) { static int a=1; Definition von a als statische Variable printf("a=%i\n",a); a++; } void main(void) { test(); test(); test(); } Was wird im Testprogramm ausgegeben? 1 2 3 ? Statische Variablen müssen bei ihrer Definition immer initialisiert werden. Wenn eine Variable als statisch definiert wurde, ist sie immer noch lokal, d.h. im Testprogramm kann in der main-Funktion nicht auf a zugegriffen werden. Nach Verlassen des Gültigkeitsbereiches bleibt jedoch der Wert der Variablen a C-Programmierung 2003/2004 erhalten. 59 59 #include <stdio.h> void test(void) { static int a; a=1; printf("a=%i\n",a); a++; } void main(void) { test(); test(); test(); } Was wird im Programm ausgegeben? 1 1 1 ? Programm 05 Im Programmbeispiel wurde die statische Variable bei ihrer Definition nicht initialisiert. Aus diesem Grund wird bei jedem Aufruf von test immer a=1 zugewiesen. Die Variable hat damit in jedem printf den Wert 1. C-Programmierung 2003/2004 60 60 8. Funktionen mit Parameterübergabe 8.1 Parameterübergabe Funktionen können Werte übergeben werden. Das void vor der Klammer des Funktionsnamens bedeutet, dass die Funktion keinen Wert zurückliefert. Das void in der Klammer nach dem Funktionsaufruf bedeutet, das der Funktion kein Wert übergeben wird. Funktionsparameter sind lokale Variablen der Funktion, die mit der Übergabe initialisiert werden. C-Programmierung 2003/2004 61 61 #include <stdio.h> /*Programm 07 */ void quadrat (int x) { int ergebnis; ergebnis=x*x; printf("Das Quadrat von %i",x); printf (" ist %i\n",ergebnis); } void main(void) { int wert; printf("bitte geben Sie eine Zahl ein: "); scanf("%i",&wert); quadrat(wert); Parameterübergabe an quadrat Wert wird an die Funktion übergeben } C-Programmierung 2003/2004 62 62 Im Beispiel hat int x den Platz von void eingenommen, d.h. die Funktion quadrat bekommt einen int Wert übergeben. Die Variable x bekommt diesen Wert zugewiesen und kann wie jeden andere Variable in der Funktion genutzt werden. Achtung! Mit der Übergabe des Wertes an die Funktion ist die Variable x initialisiert. In der Hauptfunktion main wird die Funktion quadrat aufgerufen. Im Funktionsaufruf wird wert an die Funktion übergeben und damit die Variable x in der Funktion quadrat initialisiert. Funktionsparameter verhalten sich wie lokale Variablen, d.h. Innerhalb der Funktion kann diese Variable geändert werden, ohne das sich die Variable in der übergeordneten Funktion ändert (Gültigkeitsbereich lokaler Variablen). C-Programmierung 2003/2004 63 63 8.2 Wertrückgabe #include <stdio.h> /* Programm 8 */ int quadrat (int x) { int ergebnis; ergebnis=x*x; return(ergebnis); } void main(void) { int wert,qwert; printf("bitte geben Sie eine Zahl ein: "); scanf("%i",&wert); printf ("\n\n\n"); qwert=quadrat(wert); printf("Das Quadrat von %i ist %i\n\n\n",wert,qwert); } Funktion quadrat Das int vor dem Funktionsname bedeutet, daß ein int Wert zurückgegeben wird Rückgabewert Funktionsaufruf Funktionen, die einen Wert zurückliefern, repräsentieren diesen Wert mit dem Aufruf. C-Programmierung 2003/2004 64 64 Soll eine Funktion einen Wert zurückgeben, dann erfolgt dies mit "return". Funktionen werden mit "return beendet, wenn ein Wert in die übergeordnete Funktion übergeben werden soll. Die Klammern hinter return sind nicht zwingend, erhöhen aber die Übersichtlichkeit in Programm. An den Stellen, wo im C-Programm eine Variable steht, kann auch ein berechenbarer Ausdruck stehen. #include <stdio.h> int quadrat (int x) { return(x*x); } void main(void) { int wert,qwert; printf("bitte geben Sie eine Zahl ein:"); scanf("%i",&wert); qwert=quadrat(wert); printf("Das Quadrat von %i ist %i\n",wert,qwert); } Die Berechnung erfolgt direkt in return, die Variable quadrat wird nicht benötigt C-Programmierung 2003/2004 65 65 Der Funktionsaufruf repräsentiert direkt den Wert, den die Funktion liefert. Damit kann man das Testprogramm weiter vereinfachen. #include <stdio.h> int quadrat (int x) { return(x*x); } void main(void) { int wert,qwert; printf("bitte geben Sie eine Zahl ein:"); scanf("%i",&wert); qwert=quadrat(wert); printf("Das Quadrat von %i ist %i\n",wert,quadrat(wert)); } Kann entfallen Funktionsaufruf ist direkt in die printfAnweisung integriert Wird der Rückgabewert einer Funktion nicht angegeben, geht der Compiler davon aus, dass ein int-Wert zurückgegeben wird. C-Programmierung 2003/2004 66 66 Wie würde das Programm aussehen, wenn die eben besprochene Tatsache zur Anwendung kommt??? Test #include <stdio.h> /*Programm 9 */ Das int vor der Funktion kann quadrat (int x) weggelassen werden { return(x*x); } void main(void) { int wert; printf("bitte geben Sie eine Zahl ein:"); scanf("%i",&wert); printf("Das Quadrat von %i ist %i\n",wert,quadrat(wert)); } C-Programmierung 2003/2004 67 67 8.3 Übergabe mehrerer Parameter Eine Funktion kann nur einen Wert zurückgeben (auf diese Art und Weise). Es können aber beliebig viele Werte an die Funktion übergeben werden. #include <stdio.h> int max(int a, int b) {int maxwert; if(a>b) Was realisiert dieses Programm????? {maxwert=a;} else {maxwert=b;} return(maxwert); } void main(void) {int zahl1,zahl2; printf("Bitte geben Sie Zahl 1 ein:"); scanf("%i",&zahl1); printf("Bitte geben Sie Zahl 2 ein:"); scanf("%i",&zahl2); printf("Die größere Zahl ist %i\n", max(zahl1,zahl2)); } C-Programmierung 2003/2004 68 68 8.4 Prototypen parametrisierter Funktionen ? Wann ist ein Prototyp für eine Funktion erforderlich? Ein Prototyp für eine Funktion ist dann erforderlich, wenn die Funktion hinter die main-Funktion platziert wird! Wird ein Prototyp einer Funktion benutzt, so müssen nur die Variablentypen der Funktionsparameter angegeben werden, nicht die Variablennamen. int funktion(int x, int y) { x=((x+9)*12)/y } int funktion (int, int); ! Funktion als Prototyp C-Programmierung 2003/2004 69 69 8.5 sonstiges bei Funktionen mit Parameterübergabe Funktionen, die einen Wert zurückgeben, müssen immer mit return beendet werden. ! Ist das folgendem Programmausschnitt richtig? int testfkt (int x) { if (x==20) return (x+12); } Der Compiler wird einen Fehler melden, da die Funktion einen Rückgabewert hat und return aber nur ausgeführt ´wird, wenn x=20 ist. Ist x ungleich 20, so wird die Funktion beendet und es wird kein Rückgabewert geliefert. Wie muss die Funktion richtig lauten ???? C-Programmierung 2003/2004 70 70 int testfkt (int x) { if (x==20) return (x+12); else { return (x*x) }; } C-Programmierung 2003/2004 71 71 9. Die for-Schleife Aufgabenstellung: Ausgabe der Zahlen von 1 bis 100 am Bildschirm. 1. Möglichkeit 100 printf-Anweisungen 2. Möglichkeit mit einer Schleife und einer printf-Anweisung Syntax der for-Schleife for (anweisung1 ; bedingung ; anweisung2) { anweisungsblock } C-Programmierung 2003/2004 72 72 for (anweisung1 ; bedingung ; anweisung2) { anweisungsblock} Anweisung1 Bedingung wahr? Anweisungs-block ja nein Anweisung2 C-Programmierung 2003/2004 73 73 Reihenfolge der Abarbeitung der for-Scheife 1. Anweisung1 ausführen 2. Bedingung prüfen, wenn wahr, dann weiter mit Pkt.3, sonst bei Pkt.6 3. Anweisungsblock hinter for ausführen 4. Anweisung2 ausführen 5. Weiter bei Pkt.2 6. Zur ersten Anweisung hinter den Anweisungsblock der for-Anweisung springen C-Programmierung 2003/2004 74 74 #include <stdio.h> Anweisung1 bedingung anweisung2 Programm 10 void main(void) { int x; for(x=1; x<=3; x++) { printf("Aktueller Wert :%i\n",x); } } anweisungsblock C-Programmierung 2003/2004 75 75 An jeder Stelle der for Anweisung, an der eine Variable steht, kann auch ein berechenbarer Ausdruck stehen. Innerhalb einer for Anweisung können auch mehrere Anweisungen stehen. Diese sind dann durch Komma zu trennen. #include <stdio.h> Programm 11 void main(void) { int x,y,zahl1,zahl2; printf("Bitte Zahl x eingeben :"); scanf("%i",&zahl1); printf("Bitte Zahl y eingeben :"); scanf("%i",&zahl2); for(x=1,y=zahl2; x<=zahl1; x++,y--) printf("Aktuelle Werte :%i und %i\n",x,y); } Was realisiert das Programm ??? C-Programmierung 2003/2004 ? 76 76 Initialisierungen weglassen In for Schleifen kann man auf Initialisierung von Variablen teilweise verzichten. Es ist jedoch aus Gründen der Übersichtlichkeit und Lesbarkeit des Programms nicht immer zu empfehlen. #include <stdio.h> Programm 11_04 void main(void) { int x; printf("Bitte Zahl eingeben :"); scanf("%i",&x); for(; x ; x--) printf("Aktueller Wert :%i\n",x); } An der Stelle der Bedingung steht x, das heißt, solange der wert von x ungleich Null ist, gilt die Bedingung als wahr. C-Programmierung 2003/2004 77 77 10. Schleifen mit Ein-/Austrittsbedingungen 10.1Schleifen mit Eintrittsbedingung Die Schleife in C mit Eintrittsbedingung ist die while-Schleife. Syntax: while (bedingung) {anweisungsblock } Bedingung wahr? ja anweisungsblock nein C-Programmierung 2003/2004 78 78 Ist die Bedingung wahr, dann wird der Anweisungsblock hinter while ausgeführt. Anschließend wird die Bedingung erneut geprüft. Der Anweisungsblock wird solange ausgeführt, bis die Bedingung falsch ist. Dann wird die while Schleife verlassen und das Programm fortgesetzt. #include <stdio.h> void main(void) { int zahl; Programm 11-01 Was realisiert dieses Programm? ? zahl=0; while(zahl!=200) { printf("Bitte geben Sie 200 ein:"); scanf("%i",&zahl); } } C-Programmierung 2003/2004 79 79 Auch bei der while Anweisung gilt, daß die Klammern entfallen, wenn es sich nur um eine Anweisung im Anweisungsblock handelt Eine while-Schleife ist als eine vereinfachte Form von for zu betrachten. Aus diesen Gründen kann auch eine for-Schleife mit while simulieren. for (x=5;x<=25;x++) printf ("Der Wert betraegt %i\n",x); x=5; while (x<=25) { printf ("Der Wert betraegt %i\n",x); x++; } C-Programmierung 2003/2004 80 80 Wenn man das obige Beispiel betrachtet, so kann man feststellen, dass die Lösung mit for viel einfacher erscheint. Warum also while benutzen? Die while Schleife wird für spezielle Probleme genutzt, z.B. wenn auf ein bestimmtes Ereignis gewartet wird (Syntaxprüfung bei der Eingabe von Daten/Wertebereich). In diesem Fall eignet sich die while Schleife auf Grund ihrer einfachen Konstruktion sehr gut. In einigen Anwendungsfällen kann man sogar auf den Anweisungsblock verzichten. #include <stdio.h> // Programm 11-02 int getzahl(void) { int zahl; printf("Bitte geben Sie eine Zahl ein:"); scanf("%i",&zahl); return(zahl); } void main(void) { while(getzahl()!=200); } C-Programmierung 2003/2004 81 81 10.2 Schleifen mit Austrittsbedingung: do while Bei while wird erst der Vergleich durchgeführt, d.h. der Anweisungsblock wird nach dem Vergleich abgearbeitet oder bei falscher Bedingung nicht abgearbeitet. Bei Benutzung von do while wird erst der Anweisungs-block abgearbeitet und danach die Bedingung geprüft. Problem wird bei der Eingabe mit Syntaxprüfung benutzt. Um zu prüfen, ob eine eingegebene Zahl eine gewisse Bedingung erfüllt, muß ich sie zuerst einlesen und danach prüfen. Das bedeutet, der Anweisungsblock muß erst abgearbeitet werden und danach wird erst die Bedingung geprüft. Syntax: do { anweisungsblock } while (Bedingung); C-Programmierung 2003/2004 82 82 Die do while Sckleife wird wie folgt abgearbeitet: (1) Abarbeitung des Anweisungsblockes zuwischen do und while (2) prüfen der Bedingung (3) bei wahr wird zu do gesprungen und der Anweisungsblock erneut abgearbeitet (1) (4) ist die Bedingung nicht erfüllt (falsch), so fährt das Programm hinter while fort Anweisungs-Block Bedingung wahr? ja nein C-Programmierung 2003/2004 83 83 Die while Abfrage in der do while Konstruktion wird immer mit einem Semikolon abgeschlossen. Das Programm von Seite 79 könnte mit einer do while Konstruktion wie folgt aussehen: #include <stdio.h> void main(void) { int zahl; do Keine Anfangsinitialisierung von „zahl“ notwendig { printf("Bitte geben Sie 200 ein:"); scanf("%i",&zahl); } while(zahl!=200); Programm 11_03 „Eingabeprüfung“ } In C gibt es eine spezielle Anweisung, welche nur innerhalb von Schleifen benutzt werden kann. Das heißt nur innerhalb von for, while oder do while. Das ist die continue Anweisung. C-Programmierung 2003/2004 84 84 10.3 Continue-Anweisung x=0; while (x<20) Programm 991 { x++ if (x==10) continue; printf ("x=%i\n",x); } Continue springt immer an den Anfang der innersten Schleife!! C-Programmierung 2003/2004 85 85 Beginn 1 x=0 Ausgabe von x solangeX<20 Schleifenende x=x+1 Ende x==10 nein ja In diesem Fall tritt die continue Anweisung in Aktion 1 C-Programmierung 2003/2004 86 86 Welche Auswirkung hat while (1) auf den Programmablauf? ? Die Bedingung while ist immer wahr, d.h. es ist eine Endlosschleife C-Programmierung 2003/2004 87 87 11. Logische Operatoren Der logische Operator && Der logische Operator && wir als UND oder AND-Operator bezeichnet. Er verknüpft zwei Bedingungen auf folgende Weise. Bedingung1 Bedingung2 Bedingung1 && Bedingung2 falsch falsch falsch falsch wahr falsch wahr falsch falsch wahr wahr wahr C-Programmierung 2003/2004 88 88 if ((x>=20) && (x<=40)) { anweisungsblock } Der Anweisungsblock hinter if wird nur abgearbeitet, wenn X größer gleich 20 und kleiner gleich 40 ist ? Achtung!! Der && Operator ist kommutativ, das heißt x && y ist gleichbedeutend mit y && x C-Programmierung 2003/2004 89 89 Der logische Operator || Der logische Operator || wird auch als ODER bzw. OR Operator bezeichnet. Er realisiert folgende Verknüpfungen. Bedingung1 Bedingung2 Bedingung1 || Bedingung2 falsch falsch falsch falsch wahr wahr wahr falsch wahr wahr wahr wahr Achtung!!! Der || Operator entspricht nicht dem gebräuchlichem ODER, er bedeutet, dies oder jenes oder beides Der || Operator ist ebenfalls kummutativ. Daraus folgt, dass a || b gleichbedeutend mit b|| a ! ist C-Programmierung 2003/2004 90 90 Der && Operator und der || Operator können auch gemeinsam innerhalb eines Ausdruckes auftreten. do Programm 12 { printf ("Bitte eine Zahl im Bereich von 20 bis\ 40 eingeben oder eine 0 für\ Ende der Eingabe"); scanf ("%i", &wert); } while (((wert<20) || (wert>40)) && (wert !=0)); Die Schleife wird beendet, wenn der Wert zwischen 20 und 40 liegt oder wenn eine 0 eingegeben wird. Das heißt, wenn der Wert kleiner als 20 oder größer als 40 ist und ungleich Null ist, dann wird wieder zu do gesprungen. Alle Werte zwischen 20 und 40 du die Null sind gültig!!!! C-Programmierung 2003/2004 91 91 10 wahr || falsch = wahr wahr && wahr = wahr in Schleife verbleiben!!! 30 falsch || falsch= falsch falsch && wahr = falsch Schleife verlassen 0 wahr || falsch = wahr wahr && falsch = falsch Schleife wird verlassen C-Programmierung 2003/2004 92 92 12. Variablen vom Typ float Float-Point Variablen stehen in 3 verschiedenen Varianten zur Verfügung. Typ Größe Mindestgenauigkeit Formatzeichen float 4 6 f double 8 10 f long double 10 10 lf Die Größe gibt die Byte an, die von einer Variablen belegt werden. Die Mindestgenauigkeit ist die Anzahl der Stellen hinter dem Komma. Alle Variablen decken einen Wertebereich von mindestens 1038 bis 10-38 ab. Es gibt in C keine vorzeichenlosen Gleitkommazahlen. Diese Genauigkeit der Gleitkommazahlen ist ausreichend für die meisten Operationen, auch im mathematischen Bereich. Man sollte sich jedoch immer genau den Einsatz von Gleitkommazahlen überlegen, da diese Operationen rechenintensiver und speicherintensiver als int Variablen sind C-Programmierung 2003/2004 93 93 #include <stdio.h> void main(void) { unsigned long n=3,max; //unsigned long 4 Byte Programm 13 0 bis 4.294.967.295 float pi=1.0; printf("Bis zu welchem n soll gerechnet werden:"); scanf("%li",&max); while(n<max) { pi=pi-(1.0/n)+(1.0/(n+2)); n+=4; printf("n=%lu :%f\n",n,pi*4.0); } } Dieses Programm realisiert die Berechnung der Zahl pi nach dem Verfahren von Leibnitz. pi=(1-1/3+1/5-1/7+1/9-1/11+1/13-1/15+1/17-1/19+1/21-.......)*4 Gleitkommazahlen verhalten sich in Operationen genau wie Ganze Zahlen. Es muss nur eine andere Deklaration und ein anderes Formatzeichen benutzt werden. C-Programmierung 2003/2004 94 94 Werden zwei Variablen unterschiedlichen Typs durch einen Operator verknüpft, so bekommt das Ergebnis den genaueren Typ der beiden Variablen. 1/n ist immer 0, wenn n>1 und eine int Variable. 1.0/n bekommt als Ergebnis den Wert einer Gleitkommzahl, da 1.0 als Gleitkommazahl interpretiert wird. Typumwandlung mit dem cast-Operator Mit dem cast Operator kann der Programmierer den Typ des Ergebnisses selbst bestimmen. Mit cast wird eine Typumwandlung realisiert. Syntax des cast Operators: (typ) (variable) C-Programmierung 2003/2004 95 95 int a=3, b=2; /* Beispiel-1 float c; c=a/b; Als Ergebnis hat c den Wert 1, da erst die Division mit int-Variablen durchgeführt wird. Erst danach erfolgt die Anpassung an das float Format. int a=3, b=2; / /Beispiel-2 float c; c=(float) (a) / (float) (b); Im zweiten Beispiel wird der cast Operator verwendet. Damit erfolgt erst die Anpassung an das float Format und danach die Division. Damit lautet das Ergebnis 1,5. int a=3, b=2; /* Beispiel-3 float c; c=(float) a / (float) b; C-Programmierung 2003/2004 96 96 Auch wenn die Klammern nicht geschrieben sind, erfolgt erst die Typumwandlung und danach die Division. Die Ursache liegt darin, dass der cast Operator eine stärkere Bindung hat als die Division. Deshalb erfolgt erst die Umwandlung und dann die Division C-Programmierung 2003/2004 97 97 Die Funktionen von math.h In der Bibliothek math.h sind die wesentlichsten mathematischen Funktionen enthalten. Werden diese be-nötigt, so ist diese Bibliothek am anfang des Programms anzuhänhen. #include <math.h> acos Umkehrfunktion des Kosinus asin Umkehrfunktion des Sinus atan Umkehrfunktion des Tangens ceil grundsätzliches aufrunden cos Kosinus cosh Kosinus Hyperolicus exp Exponentialfunktion (e hoch a) fabs Absolutwert von |a| floor grundsätzliches abrunden C-Programmierung 2003/2004 98 98 fmod Modulowert (a%b) frexp Aufspalten von a in a=f*2i ldexp Umkehrung zu frexp log nathürlicher Logarithmus (Basis e) log10 dekadischer Logarithmus (Basis 10) modf Aufspaltung von a in a=f+i pow Potenz a hoch b sin Sinus sinh Sinus Hyperbolicus sqrt Quadratwurzel tan Tangens tanh Tangens Hyperbolicus C-Programmierung 2003/2004 99 99 Beschreibung ausgewählter Funktionen sqrt double sqrt (double a); Berechnet die Quadratwurzel von a #include "stdio.h" #include "math.h" void main (void) {double zahl=4.3456, ergebnis; printf ("Eingabe einer Zahl: "); //scanf ("%lf",&zahl); Probleme bei %f und double zahl ergebnis=sqrt(zahl); printf ("Ergebnis von Quadratwurzel aus %f ist: %f \n",zahl,ergebnis); } C-Programmierung 2003/2004 100 100 tan double tan (double a); Berechnet den Tangens von a im Bogenmaß. sin double sin (double a); Berechnet den Sinus von a im Bogenmaß. ceil double ceil (double a) Liefert die nächst höhere Ganzzahl von a. z.B aus 6.4 wird 7, aus -3.1 wird -3 C-Programmierung 2003/2004 101 101 acos double acos (double a); Berechnung Arcus Kosinus (Umkehrfunktion von Kosinus) im Wertebereich -1 bis 1 fabs double fabs (double a) Liefert den Betrag oder Absolutwert von a ( |a| ) floor double floor (double a) Liefert die nächstniedrige Ganzzahl C-Programmierung 2003/2004 102 102 Formatzeichen Gleitkommazahlen FZ Typ Zahlensystem f double Dezimal *.***** Lf long double Dezimal *.***** e double Dezimal *.***E** Le long double Dezimal *.***E** E double *.***E** LE long double Dezimal *.***E* g double Dezimal mit oder ohne Exponent Lg long double Dezimal mit oder ohne Exponent G double Dezimal mit oder ohne Exponent LG long double Dezimal mit oder ohne Exponent Dezimal Besonderheit C-Programmierung 2003/2004 103 103 Erweiterte Formatierung der Ausgabe printf ("%.2f\n",1234.3456) .2 Legt fest, daß zwei Stellen nach dem Komma ausgegeben werden. printf ("%15.2f\n",1223.34345); 15.2 Diese Anweisung setzt 8 Leerzeichen vor die Zahl. Die Zahl selbst ist 17 Stellen lang (geplanter Wert) die auszugebende Zahl ist im Original 9 Stellen. 17 - 9 =8 ---> 8 Leerstellen. C-Programmierung 2003/2004 104 104 printf ("%-15.2f\n",1223.34345); -15.2 Zahl wird linksbündig ausgegeben. printf ("%+15.2f\n",1223.34345); +15.2 Zahl wird immer als positive Zahl ausgegeben. Programm 14 Programm 14_01 C-Programmierung 2003/2004 105 105 13. Fallunterscheidungen Gibt es zu einem Entscheidung mehrere Möhlichkeiten der Entscheidung, so müssen verschachtelte if Anweisungen oder die case-Anweisung genutzt werden. Syntax: switch (variable) { case 1: case 12: case -9: } Die Variable dient in dieser Anweisung als Schalter. Hat die Variable den entsprechenden Wert, so wird die zugehörige case Anweisung angesprungen. Hinter der Sprungmarke case muß immer eine ganzzahlige Konstante stehen.Wird eine case Marke angesprungen, so werden alle danach folgenden case Anweisungen mit abgearbeitet. Wird keine Übereinstimmung mit den Konstanten erzielt, so passiert nicht. Das Programm wird fortgesetzt. C-Programmierung 2003/2004 106 106 Sollen nach dem Ansprung einer case Marke die anderen nachfolgenden Case Anweisungen nicht abgearbeitet werden, so muß break verwendet werden. Break springt immer hinter die innerste for, switch oder while Anweisung. Beginn case 1 ? Switch/case ohne break ja Ausgabe 1 nein ja case 2 ? Ausgabe 2 nein case 3 ? ja Ausgabe 3 nein Ende C-Programmierung 2003/2004 107 107 Beginn Switch/case mit break ja Ausgabe 1 case 1 ? nein case 2 ? ja Ausgabe 2 nein case 3 ? ja Ausgabe 3 nein Ende C-Programmierung 2003/2004 108 108 #include <stdio.h> /*Programm 15 */ void main(void) { int x; printf("Geben Sie bitte 1,2 oder 3 an:"); scanf("%i",&x); switch(x) { case 1: printf("Das war Zahl-1-\n"); break; case 2: printf("Das war Zahl -2-\n"); break; case 3: printf("Das war Zahl -3-\n"); break; } } C-Programmierung 2003/2004 109 109 Mehrere case Anweisungen mit gleichem Sprungziel Beginn Es können beliebig viele case-Anweisungen das gleiche Sprungziel haben. ja case 1 ? nein ja case 2 ? Ausgabe 2 nein case 3 ? ja Ausgabe 3 nein Ende C-Programmierung 2003/2004 110 110 switch(x) { case 1: case 2: printf("Das war Zahl 2 oder 1 \n"); break; case 3: printf("Das war Zahl 3 \n"); break; } Die default-Anweisung Wenn für einen Wert keine Sprungmarke existiert, dann kann default verwendet werden. Wird also keine Übereinstimmung erzielt, dann wird immer default angesprungen. Damit kann der Sachverhalt, dass eine begrenzte Anzahl caseAnweisungen für bestimmte Bedingungen und ein Standard für "sonstiges", abgedeckt werden. Keine Sprungmarke für einen Wert default (wenn vorhanden) C-Programmierung 2003/2004 111 111 #include <stdio.h> /*Programm 15-01 */ void main(void) { int x; printf("Geben Sie bitte 1,2 oder 3 an:"); scanf("%i",&x); switch(x) { case 1: printf("Das war Zahl 1 \n"); break; case 2: printf("Das war Zahl 2 \n"); break; case 3: printf("Das war Zahl 3 \n"); break; default: printf("ungültige Eingabe \n"); break; } } C-Programmierung 2003/2004 112 112 14. Der Variablentyp char Der Variablentyp char gestattet es, mit einzelnen Zeichen zu arbeiten. char x; printf ("Bitte ein Zeichen eingeben"); scanf ("%c",&x); printf ("Das eingegebene Zeichen lautet >%c< \n",x); Das Formatzeichen ist %c Wertebereich von 0-255 (ASCII) Eine Wertzuweisung (Initialisierung) einer char-Variablen erfolgt mit: char x ; x='F'; Achtung: Wertzuweisung immer in einfachen Anführungszeichen!!!!! C-Programmierung 2003/2004 113 113 Der Variablentyp char ist fast identisch mit unsigned short. Man kann char somit auch wie ganzzahlige Variablen behandeln. x='F'; printf ("Der Wert von %c ist %i",x,x); ? Programm 15_02 int x; for (x=0;x<256;x++) printf ("Das Zeichen ist \"%c\", der Wert ist %i \n",x,x); int x; x=24+'B'; switch (x) { ASCII Tabelle Teil-1 case 'a': printf (" Ein kleines a\n"); break; case 'b': ASCII Tabelle Teil-2 printf ("Ein kleines b\n"); break; } C-Programmierung 2003/2004 114 114 Die Funktionen von ctype.h isalnum wahr für einen Buchstaben oder eine Zahl isalpha wahr für einen Buchstaben iscntrl wahr für ein bestimmtes Steuerzeichen isdigit wahr für eine dezimale Ziffer isgraph wahr für darstellbare Zeichen außer Leerzeichen islower wahr für Kleinbuchstaben isprint wahr für darstellbare Zeichen incl. Leerzeichen isupper wahr für Großbuchstaben isxdigit wahr für eine Hexadezimalzahl C-Programmierung 2003/2004 115 115 #include <stdio.h> /*Programm 15-03 */ #include <ctype.h> void main(void) {char x; printf("Bitte Zeichen eingeben:"); scanf("%c",&x); if(isdigit(x)) { printf("Es war eine dezimale Ziffer!\n"); } else { printf("Das Zeichen war keine Ziffer\n"); } } C-Programmierung 2003/2004 116 116 Die Headerdatei ctype.h stellt außerdem noch zwei Umwandlungsfunktionen zur Verfügung. tolower Umwandlung in Kleinbuchstaben toupper Umwandlung in Großbuchstaben Bei Eingaben sind diese Funktionen sehr gut zu benutzen. Soll z.B. der Buchstabe c eingegeben werden, dann sollte es egal sein, ob der Nutzer ein kleines c oder ein großes C eingibt. Die Funktionen wandeln nur, wenn es etwas zum um-wandeln gibt, sonst wird das gleiche Zeichen zurückgegeben. x=tolower(x); Die Variable x wird nur verändert, wenn der Wert von x ein Großbuchstabe ist. Dann wandelt tolower ihn in den entsprechenden Kleinbuchstaben um. Ist es bereits ein Kleinbuchstabe, dann hat tolower keine Bedeutung. C-Programmierung 2003/2004 117 117 Erläuterungen der einzelnen Funktionen isalnum int isalnum (int a); Liefert einen wahren Wert, wenn a ein Zeichen aus folgendem Wertevorrat ist: a-z, A-Z, 0-9 isalpha int isalpha (int a); Liefert einen wahren Wert, wenn a ein Zeichen aus den folgenden wertevorrat ist: a-z, A-Z oder landesspezifische Zeichen (in Deutschland äöüÄÖÜß) iscntrl int iscntrl (int a); Liefert einen wahren Wert, wenn a eins der folgenden Steuerzeichen ist: FF, NL, CR, HAT, VT, BEL oder BS C-Programmierung 2003/2004 118 118 isdigit int isdigit (int a); Liefert einen wahren Wert, wenn a eine Ziffer von 0-9 ist. isgraph int isgraph (int a); Liefert einen wahren Wert, wenn a ein darstellbares Zeichen ist, aber kein Leerzeichen. islower int islower (int a); Liefert einen wahren Wert, wenn a ein Zeichen von a-z oder ein umgebungsspezifisches Zeichen ist (Kleinbuchstabe). isprint int isprint (int a); Liefert einen wahren Wert, wenn a ein darstellbares Zeichen ist oder ein Leerzeichen. C-Programmierung 2003/2004 119 119 ispunct int ispunct (int a); Liefert einen wahren Wert, wenn isgraph (a) wahr und isalnum (a) falsch ist. isspace int isspace (int a); Liefert einen wahren Wert, wenn a ein Leerzeichen oder FF, NL, CR, HAT oder VT ist. isupper int isupper (int a); Liefert einen weahren Wert, wenn a ein Zeichen von A-Z ist oder ein umgebungsspezifischer Großbuchstabe. isxdigit int isxdigit (int a); Liefert einen wahren Wert, wenn a ein Zeichen von 0-9, a-f oder A-F ist (hexadezimaler Wertebereich). C-Programmierung 2003/2004 120 120 tolower int tolower (int a); Liefert den Kleinbuchstaben von a, falls a bereits ein Kleinbuchstabe ist, wird dieser unverändert zurückgegeben. toupper int toupper (int a); Liefert den Großbuchstaben von a, falls a bereits ein Großbuchstabe ist, wird dieser unverändert zurückgegeben. C-Programmierung 2003/2004 121 121 15. Vektoren und Zeiger Vektoren und Zeiger verhalten sich wie Konstanten und Variable zueinander. Bei Vektoren handelt es sich um konstante Adressen und bei Zeigern um variable Adressen. 000000002323432A X 412 Speicheradresse Wert Variablenname Eine Variable ist durch folgende 4 Angaben eindeutig identifiziert: • Adresse (Position) • Name (Variablenname) • Inhalt (Wert) • Größe (belegter Speicherplatz) C-Programmierung 2003/2004 122 122 Der Adreßoperator & &x liefert nicht den Inhalt der Variablen x, sondern die Adresse der Variablen x. #include <stdio.h> /* Programm 16 */ void main(void) { int x; x=100; printf("Adresse von x mit dem Wert %i ist %p\n", x, &x); x=800; printf("Adresse von x mit dem Wert %i ist %p\n", x, &x); } Formatzeichen für Adressen Was liefert das Programm für Ausgaben ? ? C-Programmierung 2003/2004 Adreßoperator & 123 123 Die Adresse einer Variablen bleibt während der Programmabarbeitung immer gleich, auch wenn sich der Inhalt ändert. Bei jedem Neustart verändert sich die Adresse, da das Programm an einer anderen Stelle im Hauptspeicher steht. Der Wert, den der Adreßoperator & liefert ist ein Vektor. Vektoren sind konstante Adressen! Der Dereferenzierungsoperator * Eine Variable, welche eine Adresse als Wert beinhaltet, nennt man einen Zeiger. Syntax für Zeigerdefinitionen: typ *variablenname C-Programmierung 2003/2004 124 124 int x, *b; Zeiger auf einen int-Wert x=99; b=&x; Dem Zeiger wird ein Vektor zugewiesen. C-Programmierung 2003/2004 125 125 Zeiger speichern Adresse, Variable speichern Werte. 000000002323432A ! X 412 Zeiger auf eine Variable 000BAC32D2FF2A b 000000002323432A int x, *b; x=99; b ist der Zeiger auf die Variable x b=&x; Dies macht aber Zeiger noch nicht interessant, wesentlich ist, wenn man die Adresse von x in b gespeichert hat, kann man darüber auf x zugreifen. C-Programmierung 2003/2004 126 126 int x, *b; /* Programm 17*/ x=99; b=&x; printf ("Adresse von x ist %p, der Wert von b ist %p", &x,b); Was wird in diesem Beispiel ausgegeben??? ? #include <stdio.h> /* Programm 18 und 18-1 */ void main(void) { int x,*z; x=10; printf("x = %i\n", x); z=&x; *z+=10; printf("x = %i\n", x); x+=10; printf("x = %i, dereferenziertes z = %i\n", x, *z); } C-Programmierung 2003/2004 127 127 Definition der int Variablen x und eines Zeigers z. Der Variablen x wird der Wert 10 zugewiesen und dieser wird in der folgenden printfAnweisung ausgegeben. Danach wird die Adresse von x dem Zeiger z zugwiesen. Jetzt wird durch eine Dereferenzierung von z (*z) nicht die in z gespeicherte Adresse von x angesprochen, sondern der Wert der an dieser Adresse steht (das ist der Wert von x). Das bedeutet, daß der Wert von x um 10 erhöht wird. In der folgenden printf-Anweisung wird der Wert von x ausgegeben. Dieser ist in diesem Fall 20. Dann wird x erneut um den Wert von 10erhöht und in der folgenden printf-Anweisung wird x ausgegeben (30) und der dereferenzierte Wert von z, also nochmals 30. Zeiger können auch direkt bei ihrer Deklaration initialisiert werden. int x=24; int *z=&x; oder int x=24 , *z=&x; C-Programmierung 2003/2004 128 128 000000002323432A X 412 000BAC32D2FF2A b b 000000002323432A 000000002323432A 000000002323432A X *b 412 000BAC32D2FF2A b 412 000000002323432A C-Programmierung 2003/2004 129 129 000000002323432A X &b 412 000BAC32D2FF2A b 000BAC32D2FF2A 000000002323432A C-Programmierung 2003/2004 130 130 Warum wird bei scanf immer der Adressoperator benutzt? ? scanf ("%i",&a); Beim Aufruf von scanf wird die Adresse übergeben, wo der einzulesende Wert abgespeichert wird. Innerhalb der Funktion scanf wird dann über eine Dereferenzierung auf die Position (Adresse) zugegriffen wo der einzulesende Wert abgespeichert wird. Würde nicht die Adresse übergeben, dann könnte man nicht auf diese variable Adresse zugreifen, da die Variable innerhalb der Funktion nicht bekannt ist. C-Programmierung 2003/2004 131 131 15.1 Adressen als Funktionsparameter In der dargestellten Form werden Zeiger nur selten verwendet, denn es ist einfacher direkt auf die Variablen zuzugreifen als über Zeiger. Im Zusammenhang mit Funktionen sind aber Zeiger sehr bedeutsam. Hauptsächlich werden Zeiger benutzt, um Adressen an Funktionen zu übergeben. Über diesen Umweg können dann direkt Variablen verändert werden. #include <stdio.h> /* Programm 19 */ Programm 19_01 void quadrat(int *wert) { Zeiger auf eine Variable wird *wert=(*wert)*(*wert); übergeben } void main(void) { int x; printf("Bitte eine Zahl eingeben:"); scanf("%i",&x); Adresse von x wird übergeben quadrat(&x); printf("Das Quadrat ist %i\n",x); } C-Programmierung 2003/2004 132 132 15.2 Zeiger auf Funktionen Zeiger beinhalten Adressen von Variablen. Es können aber in Zeigern auch Adressen von Funktionen gespeichert sein. Anwendung: - bei Parameterabhängiger Auswahl von Funktionen - in der objektorientierten Programmierung Syntax: rtype (*fkt) (para1,para2,….) Der Zeiger mit dem Namen fkt zeigt auf die Startadresse einer Funktion, die einen wert vom Typ rtyp zurückgibt und die Parameter para1, para2,… besitzt Der Funktionszeiger kann nur Adressen von Funktionen aufnehmen, die die gleichen Eingabeund Ausgabeparameter haben C-Programmierung 2003/2004 133 133 #include <stdio.h> /* Programm 990 */ void ausgabe(void) //Funktion ausgabe { printf("Bitte geben Sie eine Zahl ein:"); Programm 990_1 } (Ohne Funktionsadressen) int summe(int a, int b) // Funktion summe { return(a+b); } void main(void) { int x,y; void (*fkt1)(void); int (*fkt2)(int, int); fkt1=ausgabe; fkt2=summe; Der Name einer Funktion ohne Klammern repräsentiert ihre Adresse fkt1(); scanf("%i",&x); (fkt1)(); scanf("%i",&y); printf("Die Summe ist %i\n",fkt2(x,y)); } C-Programmierung 2003/2004 134 134 15.3 Zeiger auf Zeiger Syntax typ **name #include <stdio.h> void main(void) { int x,y,*xptr,**ptrptr; x=10; y=10; printf("x = %i\n",x); /* Programm 992 */ xptr=&x; *xptr=20; printf("x = %i, *xptr = %i\n",x,*xptr); ptrptr=&xptr; **ptrptr=30; printf("x = %i, *xptr = %i, *ptrptr = %i\n",x,*xptr,**ptrptr); *ptrptr=&y; **ptrptr=40; printf(„y = %i, x= %i *xptr = %i, *ptrptr = %i\n",y,x,*xptr,**ptrptr); } C-Programmierung 2003/2004 135 135 Übung !!! Arbeiten mit Adressen; Übergabe von Adressen an Funktionen; Übergabe mehrerer Adressen an Funktionen C-Programmierung 2003/2004 136 136 16. Variablenfelder Anstelle n Variablen zu definieren (z.B. für eventuelle Sortierungen) kann man ein Feld (array) anlegen, in welchem dann alle Werte gespeichert sind. Syntax eines Feldes: typ name[anzahl] int testfeld[20]; Felddefinition mit 20 int Werten testfeld[3]=12; Wert 3 des Feldes wird mit 12 initialisiert und Wert 17 mit 99 testfeld[17]=99; Der Feldindex beginnt immer mit 0 und reicht bis n-1, bei einem Feld mit n Elementen. C-Programmierung 2003/2004 ! 137 137 Ein Feld kann auch gleich bei seiner Definition initialisiert werden. int testfeld[5]={12,4,5,33,-88}; #include <stdio.h> /*Programm 20 */ void main(void) { int x[10],y; for(y=0;y<10;y++) { printf("Bitte Zahl %i eingeben:",y+1); scanf("%i",&x[y]); } for(y=0;y<10;y++) printf("%i multipliziert mit 2 ist %i\n",x[y],x[y]*2); } C-Programmierung 2003/2004 138 138 Felder als Funktionsparameter Der Name eines Feldes ohne [...] ist identisch mit der Startadresse des Feldes. Der Feldname ist ein Vektor. Der Zeiger auf eine Variable ist identisch mit dem Zeiger auf ein Feld. Bei Zeigern auf Felder darf bei der Benutzung des Index kein Dereferenzierungsoperator angewendet werden. C-Programmierung 2003/2004 139 139 #include <stdio.h> /*Programm 21 */ void input(int *a) { int y; for(y=0;y<10;y++) {printf("bitte Zahl %i angeben:",y+1); scanf("%i",&a[y]);} } void calc(int *a) {int y; for(y=0;y<10;y++) a[y]*=2; } void output(int *a) { int y; for(y=0;y<10;y++) printf("Das Ergebnis der %i. Multiplikation ist%i\n", } void main(void) { int x[10]; input(x); calc(x); output(x); } C-Programmierung 2003/2004 y+1,a[y]); 140 140 Mehrdimensionale Felder Die bisherigen Felder waren eindimensional. In C ist es aber auch möglich, daß mehrdimensionale Felder aufgebaut werden. int tabelle[4][10]; Mit dieser Definition wird ein zweidimensionales Feld angelegt, welches 4 Spalten und 10 Zeilen hat. Es kann dann ebenfalls jedes einzelnen Element in der Tabelle initialisiert werden. Soll z.B. in der 2. Spalte und der 8. Zeile eine 5 stehen, dann wird folgende Zuweisung notwendig: int tabelle[1][7]=5 ? Achtung!!! Das erste Feldelement beginnt immer mit der Position 0, das heißt, die Feldposition ist immer die reale Position -1. C-Programmierung 2003/2004 141 141 In C können auch noch mehr Dimensionen benutzt werden. int dreidimfeld[80][80][80]; Dieses Feld beinhaltet 80x80x80 int-Werte zu je 2 Byte. Es gilt unbedingt zu beachten, dass dieses Feld dann fast 1 MByte Speicher belegt. Auch mehrdimensionale Felder können sofort bei ihrer Deklaration initialisiert werden. Dabei erfolgt die Initialisierung immer von der letzten Dimension aus. int tabelle[2][4]={{4,5,6,7},{5,4,3,2}}; 1 2 1 4 5 2 5 4 3 6 3 4 7 2 Zeile 2 Spalten 4 Zeilen Spalte C-Programmierung 2003/2004 142 142 Initialisierung eines dreidimensionalen Feldes: int drei[2][3][4]={{{1,2,3,4},{5,6,7,8},{-2,4,-6,2}},{{12,4,5,33}, {22,33,44,55},{1,2,3,4}}}; Übung !!! Arbeiten mit Feldern; Übergabe von Feldern an Funktionen; Felder als Rückgabewert von Funktionen Verbesserung von Praktikum 1 durch Nutzung von Feldern C-Programmierung 2003/2004 143 143 17. Zeichenketten - String Mit beliebigen Variablentypen kann man Felder erzeugen. Das geht auch mit char. char[51] definiert ein Feld mit 50 Charakter-Werten. Ein String besteht aus einer Folge von Zeichen und hat eine Endekennung. In C hat die Endekennung immer den Wert 0. "erg" Stringkonstante der Länge 4 (3 Zeichen und Endekennung 0) Ein- und Ausgabe von Strings Da Strings den Status eines Feldes haben, können sie auch so behandelt werden. Sie werden besonders für E/A-Operationen genutzt. Das Formatzeichen für Strings ist %s. C-Programmierung 2003/2004 144 144 #include <stdio.h> /* Programm 22 */ void main(void) {char a[7]; a[0]='S'; a[1]='t'; a[2]='r'; a[3]='i'; a[4]='n'; a[5]='g'; a[6]=0; printf("Der erzeugte String ist :%s\n",a); } #include <stdio.h> /* Programm 23 */ void main(void) { int x; char a[7]; a[0]='S'; a[1]='t'; a[2]='r'; a[3]='i'; a[4]='n'; a[5]='g'; a[6]=0; for (x=0;a[x]!=0;x++) printf("%c \n",a[x]); } C-Programmierung 2003/2004 145 145 Das Formatzeichen %s für Strings kann auch bei scanf genutzt werden, um ganze Zeichenketten einzulesen. #include <stdio.h> //Programm 24 void main(void) {char a[81]; printf("Bitte einen max. 80 Zeichen langen text eingeben:"); scanf("%s",a); printf("Sie gaben \"%s\" ein.\n",a); } ? Debugger Vor dem a in scanf fehlt der Adreßoperator &. Das ist richtig, denn ein Feld ohne eckige Klammern repräsentiert bereits die Adresse. Damit muss in der scanf-Anweisung nicht explizit der Adreßoperator angegeben sein, denn a ist bereits die Adresse. Alternativ könnte man auch folgendes schreiben: scanf ("%s", &a[0]); Ein Character-Feld hat eine besondere Stellung. Scanf betrachtet das gesamte Feld als einen Variablentyp, als String. C-Programmierung 2003/2004 146 146 Die Funktion scanf hat die Eigenschaft, dass mehrere Worte eingelesen werden können, die durch Leerzeichen getrennt sind. Die durch Leerzeichen getrennten Worte werden als mehrere Strings aufgefasst. Um einen String einzulesen, welcher Leerzeichen enthält, kann scanf nicht verwendet werden. Die Eingabefunktion gets ist speziell für die Arbeit mit strings gedacht. #include <stdio.h> //Programm25 void main(void) {char a[81]; printf("Bitte einen max. 80 Zeichen langen text eingeben:"); gets(a); printf("Sie gaben \"%s\" ein.\n",a); } Die Funktion gets ist als Prototyp ebenfalls in der Headerdatei stdio.h enthalten. Für deren Benutzung ist also ebenfalls ein #include stdio.h notwendig. Der Funktion gets wird die Adresse des Strings übergeben, damit diese den String überschreiben kann. C-Programmierung 2003/2004 147 147 Analog zur Funktion gets gibt es auch eine Ausgabefunktion puts, welche einen String ausgibt. Auch diese Funktion steht als Prototyp in der Headerdatei stdio.h. puts ("Das ist ein Text mit puts ausgegeben.\n"); Ein String darf mit Endekennung nur gleich oder kleiner als das definierte Feld sein, niemals größer! C-Programmierung 2003/2004 148 148 Die Funktionen von string.h memchr Zeichen im Speicherblock suchen memcmp Speicherblöcke vergleichen memcpy Speicherblöcke kopieren memmove Sicheres kopieren von Speicherblöcken memset Speicherblöcke initialisieren strcat String an einen anderen hängen strchr Zeichen im String suchen strcmp Zwei Strings vergleichen strcoll Zwei Strings umgebungsunabhängig vergleichen strcpy String kopieren strerror Umwandlung eines Feldes in verbale Form strlen Länge eines String ermitteln C-Programmierung 2003/2004 149 149 strncat Teil eines String an einen anderen hängen strncmp Teile von zwei Strings vergleichen strncpy Teile eines String kopieren strrchr Zeichen von Stringende aus suchen strstr prüfen, ob String Teil eines anderen ist Erläuterungen zu den Funktionen aus string.h strcpy char *strcpy (char ziel, const char *quelle); Die Funktion strcpy kopiert den String Quelle komplett mit Endekennung in den String Ziel. Es wird der String Ziel zurückgegeben C-Programmierung 2003/2004 150 150 Arbeiten mit Strings Einem String kann bei der Initialisierung sofort ein Name zugewiesen werden. char test[10]="Beispiel"; Der String Test wird mit "Beispiel" initialisiert und hat die Länge 9 (8 Zeichen und Endekennung). Um dem String einen neuen Inhalt zu geben, muss eine Funktion aus der Headerdatei genutzt werden. Folgende Anweisung ist nicht zulässig: test="Kunde"; ! Eine Stringkonstante steht immer für ihre Adresse. strcpy (ziel,quelle); strcpy (test,"Kunde") Nutzung der Funktion strcpy (kopieren von Strings) C-Programmierung 2003/2004 151 151 Der Name eines String steht immer für seine Adresse und eine Stringkonstante steht ebenfalls für ihre Adresse. Damit ist dieser Aufruf korrekt. Stringzuweisungen außerhalb der Deklaration bedürfen immer der Nutzung von strcpy. #include <stdio.h> //Programm26 #include <string.h> void fkt(char *a) { printf("Inhalt in der Funktion :%s\n",a); strcpy(a,"Anton"); printf("Nach strcpy in der Funktion :%s\n",a); } void main(void) { char x[15]="Willibald"; printf("Inhalt vor der Funktion :%s\n",x); fkt(x); printf("Inhalt nach der Funktion :%s\n",x); } C-Programmierung 2003/2004 152 152 Der String wird in der Funktion verändert. Das hat auch Auswirkungen auf den String außerhalb der Funktion, da die Adresse des Strings übergeben wurde. Soll der String außerhalb der Funktion nicht verändert werden, so muss innerhalb der Funktion eine Kopie angefertigt werden. C-Programmierung 2003/2004 153 153 #include <stdio.h> #include <string.h> //Programm27 void fkt(char *a) { printf("In der Funktion :%s\n",a); strcpy(a,"Anton"); printf("Nach strcpy :%s\n",a); } void main(void) { char x[15]="Willibald"; printf("Vor der Funktion :%s\n",x); fkt(x); printf("Nach der Funktion :%s\n",x); } void fkt (char *a) {char b[15]; strcpy(b,a); printf ("In der Funktion: %s\n",b); strcpy (b,"Anton"); printf ("Nach strcpy : %s\n",b); } ersetzen Das Original von a bleibt erhalten!!!!! C-Programmierung 2003/2004 154 154 Ein String kann auch zeichenweise ausgegeben werden. a=0; Programm27_1 while (x[a]) printf ("%c", x[a++]); Ein String wird immer mit 0 als Endekennung abgeschlossen, damit bricht die whileSchleife bei erreichen der Endekennung ab. Steht jedoch nicht der wirkliche String zur Verfügung, sondern nur der Zeiger auf den String (ist bei Funktionsparametern üblich), muss die Zeichenweise Ausgabe wie folgt realisiert werden. char *z; z=x; while (*z); printf ("%c",*(z++)); Zeiger auf char wird definiert. Dieser Zeiger bekommt die Adresse von x (Variablenname eines String verkörpert die Adresse). Durch Dereferenzierung wird auf das Zeichen an der Adresse zugegriffen, die der Zeiger gespeichert hat. C-Programmierung 2003/2004 155 155 String-Felder Mehrere Strings können zu einem Stringfeld zusammengefasst werden. char test[5][10]; 5 String zu je 10 Zeichen Programm 28 char test[5][10]={"Otto1","Otto2","Kurt1","Kurt2","Willi"}; Auf diese einzelnen String kann problemlos zugegriffen werden: for (a=0;a<5;a++) printf ("%s\n",test[a]); ein Index Achtung!! Der Name eines Stringfeldes mit einem Index weniger als in der Definition, repräsentiert die Adresse eines speziellen String. C-Programmierung 2003/2004 156 156 18. Strukturen Variablen unterschiedlichen Typs können zu einer Gruppe zusammengefasst werden. Diese Gruppe von unterschiedlichen Variablen werden als Strukturen bezeichnet. char name[40]; char vorname[40]; int gehalt; int alter; Soll das Programm eine definierte Menge dieser Personen verarbeiten, z.B. 500, so müßte für jede Variable ein Feld definiert werden. char name[500][40]; char vorname[500][40]; int gehalt[500]; int alter[500]; Dies ist eine Realisierungsmöglichkeit, jedoch lässt sich das Problem mit Strukturen bedeutend eleganter lösen. C-Programmierung 2003/2004 157 157 struct personaldaten Struktur Personaldaten {char name[40]; char vorname[40]; int gehalt; Abschluß immer mit Semikolon int alter;}; Die einzelnen Elemente der Struktur werden auch als Member (Mitglieder der Struktur) bezeichnet. Eine Variable dieses Strukturtypes wird wie folgt definiert: struct personaldaten arbeiter1; Es wurde eine Variable arbeiter1 erzeugt, die vom Typ struct personaldaten ist. Auf die einzelnen Elemente dieser Struktur kann jetzt zugegriffen werden. Die einzelnen Variablen innerhalb der Struktur können wie normale Variablen behandelt und verarbeitet werden. Es muss nur der Variablenname der Struktur vorangestellt werden. C-Programmierung 2003/2004 158 158 arbeiter1.alter=34; arbeiter1.gehalt=6700; strcpy(arbeiter1.vorname="Otto"); strcpy(arbeiter1.name="Mueller"); Wird in der gleichen Funktion auch eine Variable mit dem Namen alter deklariert, so führt das zu keinen Konflikten, da diese Variable außerhalb der Struktur steht und damit unabhängig von der Variablen arbeiter1.alter ist. int alter=99; unabhängig von arbeiter1.alter Variablen innerhalb einer Struktur besitzen lokalen Charakter Strukturen können mit Hilfe des Zuweisungsoperators kopiert werden wie elementare Datentypen. Strukturen können in ihrer Komplexität trotzdem fast wie normale elementare Datentypen behandelt werden. C-Programmierung 2003/2004 159 159 #include <stdio.h> //Programm 29 #include <string.h> struct Personendaten {char Vorname[40]; char Nachname[40]; int Bruttogehalt; int Alter; }; void main(void) { struct Personendaten person, person2; person.Alter=34; person.Bruttogehalt=6700; strcpy(person.Vorname,"Joseph"); strcpy(person.Nachname,"Müller"); printf("%s,%s. Alter:%i Bruttogehalt:%i\n", person.Nachname, person.Vorname, person.Alter, person.Bruttogehalt); person2=person; printf("%s,%s. Alter:%i Bruttogehalt:%i\n", person2.Nachname, person2.Vorname,person2.Alter, person2.Bruttogehalt); } C-Programmierung 2003/2004 160 160 Strukturen können auch direkt bei ihrer Definition initialisiert werden. struct personaldaten arbeiter1={"Otto","Mueller",6700,34}; C-Programmierung 2003/2004 161 161 Direktes Kopieren von Feldern mittels Strukturen, da feld_1=feld_2 nicht zulässeig ist // // int feld_1[10]={1,2,3,4,5,6,7,8,9,0}; int feld_2[10]; // feld_2=feld_1; #include "stdafx.h" struct feld {int wert[10];}; Programm Test_1 int main(int argc, char* argv[]) { struct feld vfeld_1={1,2,3,4,5,6,7,8,9,0}; struct feld vfeld_2; vfeld_2=vfeld_1; printf ("%i %i %i \n",vfeld_2.wert[0],vfeld_2.wert[1],vfeld_2.wert[3]); return 0; } C-Programmierung 2003/2004 162 162 Zeiger auf Strukturen Auch auf Strukturen können Zeiger definiert werden. struct personaldaten arbeiter1; struct personaldaten *arbeiter1zgr; Zeiger auf struct arbeiter1 Die Zuweisung erfolgt dann analog wie bei anderen Variablentypen. arbeiter1zgr=&arbeiter1; C-Programmierung 2003/2004 163 163 Normaler Zugriff auf Elemente von Strukturvariablen arbeiter1.alter=99; Zugriff über Zeiger auf ein Element einer Strukturvariablen arbeiter1zgr->alter=99; "zeigt auf" Es wird nur die Variable alter aus der Struktur arbeiter1 geändert, da arbeiter1zgr auf die Struktur arbeiter1 zeigt. struct personaldaten maurer,*x; Programm 30 x=&maurer; maurer.alter=44; x->gehalt=3000; aus der Strukturvariablen Maurer wird das Element alter direkt geändert und die Variable Gehalt durch einen Zugriff über Zeiger. C-Programmierung 2003/2004 164 164 Felder von Strukturen Felder bieten in der Massendatenverarbeitung oft die Möglichkeit bestehende Probleme auf elegante Art zu lösen. int x; Definition einer int Variablen int x[100]; Definition eines int Feldes mit 100 Einträgen Diese Möglichkeit bietet sich auch bei Strukturen, wenn man ein Feld von Strukturen bildet. struct personaldaten arbeiter1; struct ersonaldaten arbeiter1[500]; Auf die Elemente dieses Strukturfeldes kann jetzt ebenfalls einzeln durch Indizierung zugegriffen werden. arbeiter1[12].alter=99; arbeiter1[455].gehalt=3900; C-Programmierung 2003/2004 165 165 Es soll das Alter der 320. Person auf 32 gesetzt werden: arbeiter1[319].alter=32; Achtung !!! Felder beginnen immer mit dem Index 0 Das Gehalt der 10. Person soll auf 4000 gesetzt werden. Der Zugriff ist mit Zeiger zu realisieren. struct personaldaten *arbeiter1zgr[500]; arbeiter1zgr=&arbeiter1; arbeiter1zgr[9]->gehalt=4000; C-Programmierung 2003/2004 166 166 Weiterhin könnte man ein Feld von Zeigern auf die Strktur personaldaten definieren. struct personaldaten arbeiter1zgr[100]; Änderung des Gehaltes der 30.Person: arbeiter1zgr[29] ->gehalt=7600; C-Programmierung 2003/2004 167 167 Verschachtelte Strukturen Strukturen können ineinander verschachtelt werden Deklaration der inneren Struktur: struct p-adressen { char strasse[100]; unsigned long plz; char ort[80] }; Einsetzen dieser Struktur in eine andere: struct personaldaten { struct p-adressen adresse; char vorname[40]; char nachname[40]; int bruttogehalt; int alter; }; C-Programmierung 2003/2004 168 168 Variable vom Typ personaldaten deklarieren: struct personaldaten person1, person2,person3; Zugriffe auf die Elemente person1.adresse.plz=07551; Beim kopieren von verschachtelten Strukturen wird sowohl die innere als auch die äußere Struktur zusammen kopiert. Damit kann ein Kopiervorgang von verschachtelten Strukturen wie folgt realisiert werden: person2=person1; person3=person1; personaldaten personaldaten p-adressen p-adressen Kopie personaldaten p-adressen C-Programmierung 2003/2004 169 169 Änderungen einer Variablen in der Struktur person1 vom Typ Personaldatenhat damit keine Auswirkungen auf andere Strukturen des gleichen Typs. Man kann die Daten der inneren Struktur aus dem vorherigem Beispiel auch außerhalb verwalten. struct personaldaten { struct p-adressen *adressen; char vorname[50]; char nachname[50]; int bruttogehalt; int alter; }; Zeiger personaldaten C-Programmierung 2003/2004 adressen 170 170 struct p-adressen homedaten; struct personaldaten daten; daten.adressen=&homedaten; Zugriff auf die Werte: daten.adressen->plz=07551; Kopieren von verschachtelten Strukturen (flache Kopie) Zeiger personaldaten adressen Kopie Zeiger Personaldaten_1 Zeiger C-Programmierung 2003/2004 171 171 Kopieren von verschachtelten Strukturen (tiefe Kopie) personaldaten Zeiger adressen Kopie Kopie Zeiger personaldaten_1 adressen_1 C-Programmierung 2003/2004 172 172 Unions Wenn in einer Struktur mehrere Variable unterschiedlichen Typs gespeichert werden sollen, aber auf Grund eines Auswahlverfahrens nur ein Typ real gespeichert wird, dann ist der Speicherplatz für die anderen Variablen verschenkt. struct werte {int intzahl; float floatzahl; Speicherplatzverschwendung,wenn nur ein Typ abgelegt wird char charwerte[20]; } union werte {int intzahl; Es kann nur ein Typ gespeichert werden, float floatzahl; entweder intzahl oder floatzahl oder charwerte charwerte[20]; } C-Programmierung 2003/2004 173 173 #include <stdio.h> //Programm 31 #include <string.h> union block { char string[20]; double dbl; int intzahl; }; void main(void) { union block sammlung; sammlung.intzahl=20; printf("int = %i\n",sammlung.intzahl); Bei einer union benutzen alle Elemente den gleichen Speicherplatz. strcpy(sammlung.string, "Johann"); printf("string = %s\n", sammlung.string); printf("int = %i\n",sammlung.integer); sammlung.dbl=3.1415926535; printf("double = %f\n",sammlung.dbl); printf("string = %s\n", sammlung.string); printf("int = %i\n",sammlung.integer); } C-Programmierung 2003/2004 174 174 Aufzählungstyp enum Der Aufzählungstyp enum ist ein Ganzzahltyp, dessen Zahlenmenge eingeschränkt ist. enum woche {MO,DI,MI,DO,FR,SA,SO}; C weist den 7 Konstanten jetzt Werte zu: MO=0 DI=1 ....SO=6 Variable definieren: Enum woche tag; tag=MI; oder tag=(woche)(2); Typumwandlung mit cast-Operator tag=XO Fehler nicht im Wertebereich tag=(woche)(20) Fehler, wird aber nicht bemerkt Schleifenbildung: int z=0; for (tag=MO;tag<=SO; tag=(woche)(++x)) C-Programmierung 2003/2004 175 175 Zuweisungen an andere Ganzzahlvariablen sind möglich: int x,y; x=DO; printf („%i“,x); Welchen Wert hat x ??????? Es muss bei bestimmten Ausdrücken auch eine Typumwandlung erzwungen werden (siehe Beispiele), dann ist aber der Programmierer dafür verantwortlich, dass der Wertebereich nicht überschritten wird. tag=(woche) (x); Ist der Wert einer Konstanten nicht explizit angegeben, ist er um 1 höher als der der Vorgängerin. Ist der Wert der ersten Konstanten nicht angegeben, dann ist er immer 0. C-Programmierung 2003/2004 ! 176 176 #include "stdafx.h" enum wochentage{MO,DI,MI,DO,FR,SA,SO}; Programm 31_1 Programm 31_1 .NET int main(int argc, char* argv[]) { enum wochentage tag,*tagzgr; tagzgr=&tag; tag=MO; printf ("%i\n\n",tag); tag=SO; printf ("%i\n\n",tag); int x; x=2; tag=(wochentage)(x); printf ("%i\n\n",tag); if (tag==MO) {printf ("Montag \n\n");} else {printf ("Nicht Montag -- anderer Wochentag\n\n");} tag=(wochentage)(0); x=0; while (tag<=SO) {printf ("%i\n", tag);x++;tag=(wochentage)(x);} printf ("\n\n"); for (tag=(wochentage)(0),x=0;tag<=SO; tag=(wochentage)(++x)) printf ("%i\n", tag); return 0; } C-Programmierung 2003/2004 177 177 Eigene Typen mit typedef Es gibt die Möglichkeit, eigene Variablentypen zu deklarieren, so dass der eigentliche Variablentyp im Programm nicht vorkommt. typdef int INTzahl; typdef unsigned int uint; typdef char Name[40]; Eine Definition einer Variablen vom Typ Name als char mit 40 Zeichen könnte dann so erfolgen: Name vorname; Eine Variable vom Typ INTzahl könnte wie folgt angelegt werden: INTzahl zahl1, zahl2, zahl3, zahl4; C-Programmierung 2003/2004 178 178 Für Strukturen gilt dies analog: typdef struct Adresse { int privatnr; int dienstnr; char ort[20]; }SAdressen; SAdressen adr; C-Programmierung 2003/2004 179 179 19. Arbeiten mit Dateien Datei Text-Datei Binär-Datei Um eine Datei zu öffnen wird die Funktion fopen benutzt. Eine Datei wird mit fclose geschlossen. Beide Funktionen stehen in der Headerdatei stdio.h. Fopen und fclose haben folgenden Aufbau: fileptr=fopen(dateiname, modus); ....... ....... fclose(fileptr); fileptr ist ein Zeiger auf eine FILE-Struktur und wird wie folgt definiert: FILE *fileptr C-Programmierung 2003/2004 180 180 Die File-Struktur enthält nach dem öffnen der Datei deren charakteristische Merkmale. Da dort ein eindeutiger Bezug zur Datei besteht, können auch mehrere Dateien geöffnet werden. Wenn filrptr nach dem Aufruf von fopen 0 ist, dann konnte die Datei nicht geöffnet werden. Die Deklaration von FILE steht ebenfalls in stdio.h. fileptr=fopen(dateiname, modus); Dateiname verkörpert den Namen der zu öffnenden Datei und wird in einer Stringkonstanten gespeichert. Der Aufbau des Dateinamens ist abhängig vom Computersystem. Modus ist ebenfalls eine Stringkonstante und gibt den Modus der zu öffnenden Datei an. Der Modus entscheidet über das Format der zu öffnenden Datei (Textdatei, Binärdatei) und darüber, ob in der Datei nur gelesen oder auch geschrieben werden soll. C-Programmierung 2003/2004 181 181 Text-Dateien Modus Beschreibung "r" öffnet eine Textdatei zum lesen "w" erstellt eine Textdatei zum Schreiben. Der Inhalt einer unter diesem Namen bereits existierenden Datei wird gelöscht. "a" Erstellt oder öffnet eine bereits existierenden Datei zum Schreiben. Der Dateipositionszeiger steht am der Datei, dadurch werden neue Daten hinten angehängt. "w+" Erstellt eine Textdatei zum Lesen oder Schreiben. Der Inhalt einer bereits unter diesem Namen existierenden Datei wird gelöscht. "a+" Erstellt oder öffnet eine bereits existierenden Datei zum Schreiben oder Lesen. Der Dateipositionszeiger steht am der Datei, dadurch werden neue Daten hinten angehängt. C-Programmierung 2003/2004 182 182 Öffnen einer Datei "test.txt" zum Schreiben: fileptr=fopen(test.txt,"w"); Die Datei wird in das aktuelle Verzeichnis geschrieben. Dieses hängt vom Computersystem und vom eingestellten Pfad auf dem Rechner ab. C-Programmierung 2003/2004 183 183 Die Funktionen fputs, fgets und feof Die Funktionen sind mit den normalem puts und gets identisch, nur dass noch zusätzlich ein Zeiger auf die zu bearbeitende Filestruktur existiert und bei fgets noch die Anzahl der maximal einzulesenden Zeichen angegeben wird. fgets (name,länge,fileptr); fputs (name,fileptr); Weiterhin wird die Funktion feof benutzt. Diese Funktion wird benötigt, um zu prüfen, ob das Dateiende erreicht ist. Dateiende erreicht: Wert von feof ist wahr Dateiende noch nicht erreicht: Wert von feof ist falsch Die Funktion feof wird folgendermaßen aufgerufen: wert=feof(fileptr); C-Programmierung 2003/2004 184 184 #include <stdio.h> #include <string.h> #define DATNAME "test.txt" //Programm 32 void schreiben(void) { FILE *fhd; char s[160]; fhd=fopen(DATNAME,"w"); if(!fhd) { printf("Datei konnte nicht erzeugt werden!\n\n"); } else { printf("Bitte maximal 160 Zeichen pro Zeile eingeben.\n"); printf("Eingabe wird mit . beendet.\n\n"); Problem getchar(); Tastaturpuffer do { printf(">>>"); gets(s); if(strcmp(s,".")) //Vergleichen von Strings { fputs(s,fhd); fputs("\n",fhd); } } while(strcmp(s,".")); fclose(fhd); printf("\nEingabe beendet!\n"); } } C-Programmierung 2003/2004 185 185 void lesen(void) { FILE *fhd; char s[160]; int x=1; fhd=fopen(DATNAME,"r"); if(!fhd) { printf("Datei konnte nicht geoeffnet werden!\n\n"); } else { printf("Die Datei hat folgenden Inhalt:\n"); fgets(s,160,fhd); do { printf("%i:%s",x++,s); fgets(s,160,fhd); } while(!feof(fhd)); fclose(fhd); printf("\nEnde der Datei!\n"); } } C-Programmierung 2003/2004 186 186 void main(void) { int input; printf("Soll die Datei 1=gelesen oder 2=beschrieben werden?"); scanf("%i",&input); if(input==1) { lesen(); } else { if(input==2) { schreiben(); } else { printf("\nFalsche Eingabe!\n\n"); } } } C-Programmierung 2003/2004 187 187 Feof liefert erst dann einen wahren Wert, wenn versucht wird, Daten zu lesen, obwohl das Dateiende erreicht ist. Es muss bemerkt werden, dass die do-Schleife im folgenden Beispiel nur dann fehlerfrei funktioniert, wenn die Datei mindestens einen Datensatz enthält. Soll auch der Fall eingeschlossen sein, dass die Datei vollständig leer sein kann, dann muß die do-Schleife noch in eine if-Schleife eigebettet werden. fgets (s,MAXZEILENLAENGE,fhd); if (!feof(fhd) { do Zuerst lesen, wenn EOF, dann wird Schleife nicht abgearbeitet. { printf("%i: %s",x++,s); fgets(s,MAXZEILENLAENGE,fhd); } while (!feof(fhd)); } fclose(fhd); Eine Funktion, die in einer Datei liest, positioniert den Dateipositionszeiger immer hinter das zuletzt gelesene Zeichen. Eine Funktion, die in eine Datei schreibt, positioniert den Dateipositionszeiger immer hinter das zuletzt geschriebene Zeichen. C-Programmierung 2003/2004 188 188 Die Funktionen fprintf und fscan Weitere Funktionen zur Bearbeitung von Textdateien sind fprintf und fscan. Die Funktionen sind identisch mit printf und scanf. printf ("Das Wort heißt %s und hat %i Buchstaben", wort,strlen(wort)); fprintf (fhd, "Das Wort heißt %s und hat %i Buchstaben", wort,strlen(wort)); Den Text, welchen printf auf den Bildschirm schreibt, bringt fprintf in eine Datei,die der File-Struktur fhd zugeordnet ist. Analog dazu holt sich fscan eine Eingabe von einer Datei, während scanf vom Bildschirm liest. Programm32_1 fscan (fhd, "%i %s", x, s); C-Programmierung 2003/2004 189 189 Die Funktionen fgetc und fputc Die Funktionen fgets und fputs lesen und schreiben Strings. Es gibt aber auch Funktionen, die ein einzelnes Zeichen aus einer Datei lesen oder schreiben, das sind fgetc und fputc. fputc (zeichen, fhd); speichert das in der Variablen zeichen gespeicherte Zeichen in die Datei, die der FileStruktur fhd zugeordnet ist. fputc liefert den wert von zeichen zurück, wenn die Übertragung erfolgreich war, bei Fehlern wird der Wert EOF zurückgegeben. wert=fputc(zeichen,fhd); if (wert==EOF) printf("Fehler beim Schreiben!!!\n"); zeichen=fgetc(fhd); Die Funktion fgetc liest ein Zeichen aus der Datei, welche der File-Struktur fhd zugeordnet ist. Danach enthält die Variable zeichen das eingelesene Zeichen. C-Programmierung 2003/2004 190 190 Binärdateien In den bisherigen Varianten wurden die Daten als Textdateien gespeichert. Damit sind sie mit jedem beliebigen Editor lesbar. Oft ist es aber günstigen (z.B. aus Speicherplatzgründen) die Daten direkt als Binär-Daten zu speichern. Ein Umwandeln von Textdaten in Zahlenvariablen kann bei der Speicherung als Binärdaten ebenfalls entfallen. C-Programmierung 2003/2004 191 191 Modus Beschreibung "rb" öffnet eine Binärdatei zum Lesen "wb" erstellt eine Binärdatei zum Schreiben. Der Inhalt einer bereits bestehenden Datei wird gelöscht. "ab" erstellt oder öffnet eine bereits bestehende Binärdatei zum Schreiben. Der Dateipositionsanzeiger steht am Ende der Datei, d.h. Die Daten werden angehängt. "rb+" öffnet eine Binärdatei zum Lesen oder Schreiben "wb+" erstellt oder öffnet eine Binärdatei zum Lesen oder Schreiben. Der Inhalt einer bereits bestehenden Datei wird gelöscht. "ab+" erstellt oder öffnet eine Binärdatei zum Lesen oder Schreiben. Der Dateipositionsanzeiger steht am Ende der Datei, d.h. Die Daten werden angehängt. C-Programmierung 2003/2004 192 192 Die Funktionen fwrite und fread Syntax von fwrite: anz=fwrite (adresse, groesse, anzahl, fhd); adresse ist die Adresse, an der die zu speichernde Variable oder das zu speichernde Feld steht. groesse ist die Größe der Variablen oder die eines Feldelements in Byte. anzahl ist bei einer Variablen 1, bei einem Feld die Anzahl der Elemente. fhd ist der Zeiger auf die FILE-Struktur, mit der die zu beschreibende Datei verknüpft ist. fwrite gibt die Anzahl der komplett geschriebenen Elemente zurück. C-Programmierung 2003/2004 193 193 Syntax von fread: anz=fread (adresse, groesse, anzahl, fhd); adresse ist die Adresse an der die aus der Datei gelesenen Daten gespeichert werden. Alle anderen Parameter sind analog fwrite. C-Programmierung 2003/2004 194 194 Die Funktion sizeof Die besprochenen Funktionen bergen zur Zeit noch einige Probleme in sich, denn die Größe der Variablen ist in den meisten Fällen unbekannt. Selbst bei int-Werten ist es von System zu System verschieden, wie viel Byte zur Speicherung benutzt werden. Um das Problem zu beseitigen, wurde die Funktion sizeof geschaffen. Sie bestimmt die Größe einer jeden Variablen und liefert damit den benötigten Wert für die Lese- und Schreibfunktionen für Binärdateien. char s[80]; //Programm 37 strcpy (s,"Otto"); printf ("Die Größe von s ist %i \n",sizeof (s)); printf ("Der Variablentyp int ist %i Bytes gross. \n",sizeof (int)); Im ersten Fall wird 80 ausgegeben, da sich sizeof mit der Definition von s befasst und nicht mit der Zuweisung des Inhaltes. Im zweiten Fall wird die Größe des int Variablentypes ermittelt und mit printf ausgegeben. C-Programmierung 2003/2004 195 195 #include <stdio.h> //Programm 33 #include <string.h> #define DATNAME "test.txt" #define MAXVORNAME 25 #define MAXNACHNAME 20 void schreiben(void) {FILE *fhd; char vname[MAXVORNAME],nname[MAXNACHNAME]; unsigned int alter,groesse; fhd=fopen(DATNAME,"wb"); if(!fhd) { printf("Datei konnte nicht erzeugt werden!\n\n"); } else { printf("Vorname (Max. %i Zeichen) :",MAXVORNAME-1); scanf("%s",vname); printf("Nachname (Max. %i Zeichen) :",MAXNACHNAME-1); scanf("%s",nname); printf("Alter in Jahren :"); scanf("%u",&alter); printf("Groesse in Zentimetern :"); scanf("%u",&groesse); fwrite(vname,sizeof(vname),1,fhd); fwrite(nname,sizeof(char),MAXNACHNAME,fhd); fwrite(&alter,sizeof(alter),1,fhd); fwrite(&groesse,sizeof(unsigned int),1,fhd); fclose(fhd); printf("\nEingabe beendet!\n"); }} C-Programmierung 2003/2004 196 196 void lesen(void) { FILE *fhd; char vname[MAXVORNAME],nname[MAXNACHNAME]; unsigned int alter,groesse; fhd=fopen(DATNAME,"rb"); if(!fhd) { printf("Datei konnte nicht erzeugt werden!\n\n"); } else {fread(vname,sizeof(vname),1,fhd); fread(nname,sizeof(nname),1,fhd); fread(&alter,sizeof(alter),1,fhd); fread(&groesse,sizeof(groesse),1,fhd); fclose(fhd); printf("Vorname : %s\n",vname); printf("Nachname : %s\n",nname); printf("Alter in Jahren : %u\n",alter); printf("Groesse in cm : %u\n",groesse); } } C-Programmierung 2003/2004 197 197 void main(void) { int input; printf("Soll die Datei 1=gelesen oder 2=beschrieben werden?"); scanf("%i",&input); if(input==1) { lesen(); } else { if(input==2) { schreiben(); } else { printf("\nFalsche Eingabe!\n\n"); } } } C-Programmierung 2003/2004 198 198 Dateipositionsanzeiger Für Binärdateien gibt es die Möglichkeit ein spezielles Element aus der Datei zu bearbeiten. Das setzt natürlich die Veränderung des Dateipositionszeigers voraus. Nur dadurch kann eine spezielle Stelle innerhalb einer Datei erreicht werden. Der Dateipositionszeiger wird mit der Funktion fsetpos verändert. Syntax von fsetpos: fpos_t position; a=fsetpos(fhd,&position); Die Variable a enthält den Wert 0, wenn das Setzen des Dateipositionszeigers erfolgreich war. Die Adresse der Variablen Position vom Typ fpos_t wird an die Funktion fsetpos übergeben (fpos_t ist ein selbst deklarierter Variablentyp). Fsetpos löscht auch das Dateiendeflag. C-Programmierung 2003/2004 199 199 Programm 33_1 void lesen2(void) { FILE *fhd; char vname[MAXVORNAME],nname[MAXNACHNAME]; unsigned int alter,groesse; fpos_t position; fhd=fopen(DATNAME,"rb"); if(!fhd) { printf("Datei konnte nicht erzeugt werden!\n\n"); } else { position=sizeof(vname)+sizeof(nname)+sizeof(alter); fsetpos(fhd,&position); fread(&groesse,sizeof(groesse),1,fhd); fclose(fhd); printf("Groesse in cm : %u\n",groesse); } } C-Programmierung 2003/2004 200 200 Es wird eine Variable mit dem Namen position definiert. In ihr wird die Summe der Längen der zu überspringenden Daten gespeichert. Nach dem Öffnen der Datei wird mit fsetpos der Dateipositionszeiger an die Stelle vor den gespeicherten Daten der Größe gesetzt. Alle anderen Anweisungen sind dann analog dem Beispiel 33. Die Variablen vname, nname, alter wurden definiert, aber nur zur Überspringung der Daten benutzt. Der Compiler kann hier eine Warnung ausgeben, da die Variablen nicht benutzt wurden. Diese Warnung kann ignoriert werden. C-Programmierung 2003/2004 201 201 Das Gegenstück zu fsetpos ist fgetpos. Diese Funktion speichert den aktuellen Wert des Dateipositionszeigers in einer Variablen. Syntax: fpos_t position; a=fgetpos(fhd, &position); Jetzt wird der aktuelle Inhalt des Dateipositionszeigers in die Variable position geschrieben. Ist die Funktion erfolgreich ausgeführt, dann gib die Funktion den Wert 0 zurück, welcher dann in der Variablen a gespeichert ist C-Programmierung 2003/2004 202 202 Eine weitere Funktion zur Veränderung des Dateipositionszeigers ist fseek. Diese Funktion kann in verschiedenen Modi aufgerufen werden. Syntax von fseek: a=fseek(fhd, distanz, modus); SEEK_SET Der Dateipositionszeiger wird auf Dateianfang gesetzt und bekommt den Wert von distanz hinzuaddiert. Damit sind nur positive Werte sinnvoll. Der Aufruf ist analog fsetpos(fhd, &position), wenn position und distanz gleich sind. SEEK_END Der Dateipositionszeiger wird auf Dateiende gesetzt und bekommt den Wert von distanz hinzuaddiert. Es sind nur negative Werte für distanz sinnvoll. SEEK_CUR Zum Dateipositionszeiger wird der Wert von distanz addiert. Damit sind positive und negative Werte sinnvoll. C-Programmierung 2003/2004 203 203 fgetpos (fhd, &position); position+=distanz; fsetpos (fhd, &position); Dateipositionsteiger um 2 Byte nach vorn bewegen: fseek (fhd,2,SEEL_CUR); Die Modi für fseek sind Makros, die in der Datei stdio.h definiert sind. Die Funktion fseek liefert den Wert 0 zurück, wenn der Dateipositionszeider fehlerfrei verändert wurde. Fseek löscht ebenfalls das Dateiendeflag. Die Funktion rewind setzt den Dateipositionszeiger auf den Anfang der Datei und löscht das Fehlerflag. Der Aufruf erfolgt mit: rewind (fhd); Die Funktionen fsetpos, fgetpos und fseek spezifizieren einen eventuell aufgetretenen Fehler im Makro errno. C-Programmierung 2003/2004 204 204 Weitere Funktionen zur Arbeit mit Dateien rename #include <stdio.h> /* Programm 38 */ void main(void) { printf("Datei wird umbenannt"); rename("test.txt","mueller.txt"); /* Datei wird von */ /* test.txt */ /* nach mueller.txt umbenannt */ } C-Programmierung 2003/2004 205 205 remove remove löscht eine Datei namens name und gibt bei Erfolg den Wert 0 zurück. Name ist die Adresse eines String. a=remove (name); remove ("test.txt") löscht die Datei test.txt C-Programmierung 2003/2004 206 206 Oft werden im Programmen Dateien zur Zwischenspeicherung benötigt, die nach Programmende gelöscht werden können. Dabei ist besonders wichtig, dass ein Dateiname gewählt wird, der einzigartig ist, um das Überschreiben bzw. Zerstören anderer Dateien zu vermeiden. Dies kann mit der Funktion tmpname erfolgen, diese Funktion erzeugt einen Filenamen, der einzigartig ist. Syntax: char a[80]; tmpnam (a); Die Datei kann dann mit fopen geöffnet werden. char a[80]; tmpnam (a); fhd=fopen(a,"wb+"); C-Programmierung 2003/2004 207 207 Für die Aufgabe der Erzeugung einer temporären Datei kann auch die Funktion tmpfile genutzt werden. Diese Funktion öffnet eine binäre Datei zur Ein- und Ausgabe und liefert die Adresse der File-Struktur zurück, über die die Datei verwaltet wird. fhd=tmpfile(); Wenn Fehler innerhalb der Dateiarbeit auftreten, dann kann die Funktion ferror genutzt werden. a=ferror(fhd); Bei aufgetretenen Fehlern ist a dann ungleich Null. Diese Funktion kann genutzt werden, um Fehler auszutesten und entsprechende Meldungen zu erzeugen. C-Programmierung 2003/2004 208 208 Anwendung der Funktion system zum löschen des Konsolenfensters. #include <stdio.h> /* Programm 39 */ #include <conio.h> #include <stdlib.h> void main(void) { int zahl; zahl=0; while(zahl!=200) { printf("Bitte geben Sie 200 ein:"); scanf("%i",&zahl); //clrscr (); system("cls"); printf("Bitte geben Sie 900 ein:"); scanf("%i",&zahl); //clrscr(); } } C-Programmierung 2003/2004 209 209 20. Präprozessor-Befehle Ein Präprozessor-Befehl, #include, wurde bereits in vielen Programmen genutzt. Der Präprozessor kann jedoch noch bedeutend mehr Unterstützung für den Programmierer bieten. #define ohne Parameter Die define Anweisung erlaubt es, dass einem bestimmten Wert ein Name zugewiesen wird. #define MAXWERT 80 Dem symbolischen Name MAXWERT wird der Wert 80 zugeordnet. An jeder Stelle im Programm, an der MAXWERT auftaucht wird dies durch den Präprozessor mit 80 ersetzt. void main (void) {char feld[MAXWERT]; ....... ;} Der String wird in diesem speziellen Fall 80 Zeichen groß. Die define Anweisung arbeitet auf Textebene, aus diesem Grund ist auch folgendes Programm richtig: #define START main void START (void) C-Programmierung 2003/2004 210 210 Bevor der Compiler den Quelltext bearbeitet, ersetzt er START durch main. Damit ist das C-Programm in Ordnung und es wird zu keinen Fehlern während der Compilierung kommen. Weitere Anwendungen von #define sind: #define STRING "Tabelle" void main (void) {char Feld[80]=STRING;} #define WERTE 80+80 void main (void) {int x; x=WERTE*2; ...........;} #define WERTE (80+80) void main (void) {int x; x=WERTE*2;...........;} C-Programmierung 2003/2004 211 211 #define mit Parameter Es können auch Makros mit Parameter definiert werden. #define QUADRAT(x) (x*x) Wenn der Präprozessorjetzt auf QUADRAT(ausdruck) stoßt, dann ersetzt er dies durch (ausdruck)*(ausdruck). #define QUADRAT(x) (x*x) //Programm 36 void main (void) {int zahl; printf ("Bitte einen Wert eingeben:"); scanf ("%i", &zahl); printf ("Das Quadrat von %i ist %i \n",zahl, QUADRAT(zahl));} printf ("Das Quadrat von %i ist %i \n",zahl, QUADRAT(zahl++)); ? Was berechnet das Programm, wenn die Zahl 4 eingegeben wird und wie groß ist der Wert von zahl am Programmende? Der Präprozessor ersetzt zahl++ * zahl++. Damit lautet das Ergebnis 16 (4*4) und zahl ist am Programmende 6. C-Programmierung 2003/2004 212 212 #undef Mit der Anweisung #undef kann eine bestehende #define-Anweisung aufgehoben werden. Das kann z.B. notwendig sein, wenn ein definiertes Makro nur in einem bestimmten Bereich gültig sein soll, oder wenn Makros aus bestehenden Header-Files nicht genutzt werden sollen, oder durch eigene Definitionen ersetzt werden müssen. #define STRING "Tabelle" ............ #undef STRING #define STRING "MUSTER" #include Die #include-Anweisung wird schon seit längeren in Programmen genutzt. #include <stdio.h> Die spitzen Klammern bedeuten, dass die Header-Datei im include Pfad des Compilers zu suchen ist. Der Nutzer kann aber auch eigene Header-Dateien schreiben, in welchen die ganzen Deklarationen stehen. Die eigenen Header-Dateien speichert man im gleichen Verzeichnis wie das Programm. Möchte man eine Header-Datei einbinden, die im gleichen Verzeichnis wie das Programm steht, benutzt man doppelte Anführungszeichen (#include "header1"). C-Programmierung 2003/2004 213 213 Die #include-Anweisung kann natürlich auch in Verbindung mit #define benutzt werden. #define STD <stdio.h> #define EIG "eigen.h" #include STD #include EIG C-Programmierung 2003/2004 214 214 #if und #endif Die #if Anweisung entspricht der C-if-Anweisung, nur dass es sich auf eine Definition des Präprozessors bezieht. Diese Anweisung ist hauptsächlich zum bedingten Compilieren notwendig. Es werden bestimmte Anweisungen nur dann kompiliert, wenn die entsprechenden Randbedingungen erfüllt sind. #if TEST == 1 print ("Das ist ein Test - Makro Test ist 1"); x++; #endif Die Anweisungen im Beispiel zuwischen #if unf #endif werden nur dann kompiliert, wenn das Makro den Wert 1 hat. Die #endif Direktive ist unbedingt notwendig und schließt jede #if Direktive ab. Mit diesen Anweisungen ist es z.B. möglich, dass ein Programm geschrieben wird und vor dem kompilieren wird entschieden in welcher Sprache das Programm übersetzt wird. C-Programmierung 2003/2004 215 215 #include <stdio.h> // Programm 34 #define DEUTSCH 1 #define ENGLISCH 2 #define WERT DEUTSCH void main(void) { int zahl; #if WERT == DEUTSCH printf("Bitte Wert eingeben:"); #endif #if WERT == ENGLISCH printf("Please enter a value:"); #endif scanf("%i",&zahl); } C-Programmierung 2003/2004 216 216 Vordefinierte Makros __DATE__ Makro steht für eine Stringkonstante mmm tt jjjj Programm 43 Startdatum der Kompilierung __TIME__ Makto steht für das Datum hh:mm:ss Startzeit der Kompilierung __FILE__ Stringkonstante mit dem aktuellen Namen der Quellcodedatei __LINE__ Aktuelle Zeilennummer Ganzzahl __STDC__ Liefert Ganzzahl 1, wenn Compiler ein ANSI C Compiler ist (geht nicht bei allen Compilern) C-Programmierung 2003/2004 217 217 21. Programmier-Technologien C-Programmierung 2003/2004 218 218 Headerdatei-1.h xx-1.cpp Headerdatei-2.h Headerdatei-3.h xx-2.cpp Headerdatei-4.h xx-3.cpp xx-4.cpp xx-3. obj xx-4. obj Compiler xx-1. obj xx-2. obj Linker Programm xx.exe C-Programmierung 2003/2004 219 219 21.1 Eigene Header-Dateien - Programme modular aufbauen - Möglichkeit der eigenen Header-Dateien nutzen - Auslagerung in Header-Dateien: - alle #define Anweisungen - Struktur- und Uniondeklarationen - Prototypen Bei doppelten Anführungszeichen stehen die Headerdateien im aktuellen Verzeichnis #include <stdio.h> Programm 40 #include <string.h> #include "eigene-header-datei.h" C-Programmierung 2003/2004 220 220 Bei Headerdateien sollte unbedingt verhindert werden, dass sie mehrfach kompiliert werden. Dies kann in komplexen Projekten zu Fehlern führen. Um das zu vermeiden, wird die Präprozessoranweisung #ifndef benutzt. #ifndef __HEADERDATEI001_H #define __HEADERDATEI001_H Namenskonventionen von C #include ……. . . #endif siehe auch Programm 40 C-Programmierung 2003/2004 221 221 Statische Variablen und Funktionen Statische Variablen Programm 41 static int rekfakul (int x, int y) Programm 42 {if (x==1) return (y); return (rekfakul (x-1,y*(x-1))); } int fakul (int x) {if x<0) return (0); if (x==0) return(1); return (rekfakul (x,x)); C-Programmierung 2003/2004 222 222 Hinweise zur modularen Programmierung: - Funktionen zu sinnvollen Modulen zusammenstellen - Themenbezogene Strukturierung - Schnittstellen zu den Funktionen transparent darstellen und dokumentieren - wenn möglich auf globale Variablen verzichten - Informationen zu den Funktionen hinzufügen (Kommentarzeilen), in denen die Funktion erklärt wird - Module genau beschreiben, Zusammenhänge eindeutig erläutern - Aufbau von eigenen Header-Dateien für Definitionen - Angabe von Änderungsständen und Versionsnummern - Nutzung der Readme-Files der Entwicklungsumgebung C-Programmierung 2003/2004 223 223 21.2. Rekursion Von Rekursionen spricht man, wenn sich Funktionen selbst aufrufen. // // #include <stdio.h> Rekursion01.cpp void rekfunk (void) {int x=99; static int z=1; printf ("Ausgabe innerhalb der Funktion rekfunk beim %i. Aufruf mit Wert %i\n",z,x); z++;rekfunk(); } Absturz des Programms durch Stack-Übrlauf void main(void) { • immer neue Rücksprungadresse von rekfunk rekfunk(); • immer neue Lokale Variable x } C-Programmierung 2003/2004 224 224 Sinnvolle Anwendung von rekursiven Funktionsaufrufen. Beispiel: Berechnung der Fakultät einer Zahl 3!= 1*2*3=6 4!=1*2*3*4=24 4!=1*2*3*4*5=120 #include <stdio.h> // Programm: Fakultaet01.cpp void main(void) {int x; Berechnung ohne rekursiven do Funktionsaufruf { int i,erg=1,z; printf ("Bitte die Zahl eingeben, von der die Fakultät berechnet werden soll: "); scanf ("%i",&i); if (!(i==0)) { for (z=1;z<i+1;z++) erg=erg*z; } printf ("Die Fakultaet von %i ist %i\n\n",i,erg); printf ("Soll eine weitere Berechnung durchgefuehrt werden? j=1/n=2: "); scanf ("%i",&x); } } while (x==1); C-Programmierung 2003/2004 225 225 Berechnung auch wie folgt möglich: #include <stdio.h> x!=x*(x-1)! ; mit 0!=1 // Programm: Fakultaet02.cpp int fakultaet (int x) { if (x<0) return (0); if (x==0) return (1); return (x*fakultaet (x-1)); } void main(void) {int wert,x,erg; do { printf ("Bitte die Zahl eingeben, von der die Fakultät berechnet werden soll: "); scanf ("%i",&wert); erg=fakultaet (wert); printf ("Die Fakultaet von %i ist %i\n\n",wert,erg); printf ("Soll eine weitere Berechnung durchgefuehrt werden? j=1/n=2: "); scanf ("%i",&x); } while (!(x==2)); } C-Programmierung 2003/2004 226 226 Programm am Beispiel von Fakultät 4: Aufruf von fakultaet (4): x>0 Æ Rückgabe von 4*fakultaet (3); Aufruf von fakultaet (3): x>0 Æ Rückgabe von 3*fakultaet (2); Aufruf von fakultaet (2): x>0 Æ Rückgabe von 2*fakultaet (1); Aufruf von fakultaet (1): x>0 Æ Rückgabe von 1*fakultaet (0); Aufruf von fakultaet (0): x==0 Æ Rückgabe von 1; Rücksprung in fakultaet (1): Rückgabewert von 1*1=1 Rücksprung in fakultaet (2): Rückgabewert von 2*1=2 Rücksprung in fakultaet (3): Rückgabewert von 3*2=6 Rücksprung in fakultaet (4): Rückgabewert von 4*6=24 Ausgabe Ergebnis: 24 C-Programmierung 2003/2004 227 227 Türme von Hanoi 0 A B C 1 A B C 2 A B C 3 A B C 4 A B C 5 A B C 6 A B C 7 A C-Programmierung 2003/2004 B C 228 228 21.3. Listen Abstrakter Datentyp: Ein abstrakter Datentyp ist ein Datentyp, dem zusätzlich Funktionen zur Verfügung gestellt werden, die auf ihm operieren. Sortierung von Warteschlangen: Æ jedes Mitglied der Warteschlange merkt sich seinen Vorgänger Æ Länge der Schlange ist nicht relevant • ideal für dynamische Speicherverwaltung, denn der für die Speicherung notwendige freie zusammenhängende Platz ist nicht größer als die Größe eines einzelnen Elementes C-Programmierung 2003/2004 229 229 Einfach verkettete Listen struct Telefon { char name[80]; char vorname[80]; char telefon[25]; char mail[50]; } Es ist effektiv, die Daten immer in einer Struktur zu kapseln Æ Trennung zwischen Nutzdaten und Kontrolldaten Ein Listenelement benötigt zuerst die Nutzdaten, d.h. die Struktur telefon und die Information auf das vorherige Element, d.h. einen Zeiger auf den Nachfolger struct Knot {struct Telefon *telefon; //Zeiger auf ein Element vom gleichen Typ struct Knot *next; // Zeiger auf nächstes Element }; C-Programmierung 2003/2004 230 230 C-Programmierung 2003/2004 231 231 C-Programmierung 2003/2004 232 232 C-Programmierung 2003/2004 233 233 Listenkopf: struct Knot *listenkopf=0; Wird mit 0 initialisiert, da am Anfang noch kein Element existiert 1. Funktion der Liste –anhängen eines Knotens an die Liste -Parameterübergabe - Nutzdaten und Listenkopf -Funktion erzeugt einen Knoten und hängt ihn an die Liste an - gibt Wert zurück (Funktion ausgeführt oder Error) Funktionen malloc und free Anfordern von Speicher: void *malloc (size_t groesse); - der Typ der Speicherplatzanforderung ist size_t - sizeof liefert einen Wert vom Typ size_t zurück Æ Nutzung bei malloc Freigeben von Speicher: void free (void *ptr); C-Programmierung 2003/2004 234 234 Nutzung von malloc und free #include <stdio.h> #include <stdlib.h> //Speicher01.cpp void main(void) { void *nullptr; int *ptr; nullptr=malloc( sizeof(int) ); ptr=(int*)(nullptr); if(ptr) { *ptr=30; printf("Der dynamische Speicherblock hat den Wert %i gespeichert\n",*ptr); free(ptr); } } C-Programmierung 2003/2004 235 235 - Funktion malloc gibt einen Zeiger auf void zurück, d.h. auf einen typenlosen Zeiger - ein typenloser Zeiger kann auf alles zeigen - für die weitere Nutzung muss aber dieser void Zeiger durch Typumwandlung mit dem cast Operator in einen typisierten Zeiger gewandelt werden - der von malloc gelieferte Zeiger ist die Adresse des reservierten Speicherblocks oder der Nullzeiger Nullzeiger: - beinhaltet die Adresse 0; ist per Definition ein leerer Zeiger, d.h. er zeigt auf kein Element - wird bei Funktionen dazu benutzt, um zu zeigen, dass kein Element vorhanden ist oder erzeugt wurde (nullptr) C-Programmierung 2003/2004 236 236 int ListAppend (struct Knot *hd, struct Telefon *tel) {void *ptr; ptr=malloc(sizeeof(struct Knot)); struct Knot *cur,*ap; ap=(struct Knot *)(ptr) if (ap) { ap->telefon=tel; ap->next=0; if (!hd) {hd=ap; return (1);} else {cur=hd; while (cur->next) cur=cur->next; cur->next=ap; return (1);} } return (0); } C-Programmierung 2003/2004 237 237 1. Definition von 2 Zeigern, *ap bekommt dann einen Speicherbereich in der Größe vonstruct Knot zugewiesen 2. prüfen, ob hd wirklich Speicherplatz zugewiesen wurde, wenn ja ÆVerweis auf Nutzdaten hergestellt und Verweis auf das nächste Element wird 0 (letztes Element-> hat keinen Nachfolger 3. prüfen, ob hd 0 ist, wenn ja Æes existiert noch kein Knoten, dieser wird der erste 4. ist hd nicht 0 Æ es existiert mindestens ein Knoten Æ letzten Knoten finden, um neues Element anhängen zu können 5. cur bekommt die in hd gespeicherte Adresse und zeigt damit auch auf den ersten Knoten 6. Über while Schleife wird geprüft, ob der next-Zeiger des Knotens, auf den cur zeigt gleich 0 ist Æ bei ja ist dies der letzte Knoten, bei nein gibt es weitere Knoten 7. Ist cur 0 wird while abgebrochen, bei ungleich 0 wird weiter gesucht 8. Ist die while Schleife abgebrochen Æ cur zeigt auf das letzte Listenelement 9. Dann wird dessen next auf den neuen Knoten gesetztÆ Liste ist um ein Element länger Nachteil: Um das letzte Element zu finden, muss die gesamte Liste durchsucht werden. C-Programmierung 2003/2004 238 238 Effektiver: direkter Zugriff auf Anfang und Ende der Liste Æ Einführung einer Listenkopfstruktur struct ListHead {struct Knot *head; struct Knot tail;} Zeiger auf den Listenkopf: struct ListHead *liste; struct ListHead *ListCreate(void) // Funktion zum Anlegen und initialisieren des {void *ptr3; // Listenkopfes ptr3=malloc(sizeof(struct ListHead)); struct ListHead *head; head=(struct ListHead *)(ptr3); if(head) { head->head=head->tail=0; return(head); } return(0); } C-Programmierung 2003/2004 239 239 Anpassen der ListAppend Funktion unter Nutzung des Listenkopfes: int ListAppend(struct ListHead *hd, struct Telefon *tel) // ListAppend ********* {void *ptr1; ptr1=malloc(sizeof(struct Knot)); struct Knot *ap; ap=(struct Knot *)(ptr1); if(ap) { ap->telefon=tel; ap->next=0; if(!(hd->head)) { hd->head=hd->tail=ap; return(1); else } { hd->tail->next=ap; hd->tail=ap; return(1); } } return(0); } C-Programmierung 2003/2004 240 240 Funktion zum Löschen der Liste und freigeben des Speichers void ListDelAll(struct ListHead *hd) { struct Knot *cur=hd->head, *nex; //ListDelAll *************** while(cur) { nex=cur->next; free(cur->telefon); free(cur); cur=nex; } free(hd); } Die Funktion geht die gesamte Liste durch. Bevor ein Knoten gelöscht wird prüft die Funktion, ob dieser einen Nachfolger hat; wenn ja, dann wird dessen Adresse in einer Variablen gespeichert. Beim Löschen wird ja der Verweis auf den Nachfolger ebenfalls gelöscht. Zuerst wird die Nutzdatenstruktur, dann die Knoten und zuletzt der Listenkopf gelöscht. C-Programmierung 2003/2004 241 241 Die Funktion zu Dateneingabe: int ListInput(struct ListHead *hd) //ListInput ***************** {void *ptr2; ptr2=malloc(sizeof(struct Telefon)); struct Telefon *dat; dat=(struct Telefon *)(ptr2); if(dat) { printf("Vorname :"); gets(dat->vorname); printf("Nachname :"); gets(dat->nachname); printf("Telefon :"); gets(dat->telefon); if(ListAppend(hd,dat)) {return(1); } else {free(dat); return(0);} } else { return(0); } } C-Programmierung 2003/2004 242 242 Die Funktion zum Anzeigen der Liste: void ListShow(struct ListHead *hd) { struct Knot *cur=hd->head; //ListShow ****************** if(!cur) { printf("Die Liste ist leer!\n"); return; } while(cur) { printf("Name: %s, %s. Tel.:%s\n",cur->telefon>nachname,cur->telefon->vorname,cur->telefon->telefon); cur=cur->next; } } C-Programmierung 2003/2004 243 243 Die main-Funktion void main(void) { struct ListHead *liste; int eingabe; liste=ListCreate(); if(liste) { do { printf("\n1 = Liste zeigen\n"); printf("2 = Knoten anfuegen\n"); printf("\n0 = Ende\n\n"); printf("Eingabe:"); scanf("%i",&eingabe); getchar(); printf("\n\n"); switch(eingabe) { case 1: ListShow(liste); break; case 2: ListInput(liste);break; } } while(eingabe); ListDelAll(liste);} } C-Programmierung 2003/2004 244 244 21.4. Sortierverfahren Elementare Sortierverfahren: - Selection Sort - Insertion Sort - Bubble Sort - Shell Sort - Distribution Counting - Quick Sort Digitales Sortieren: - Radix Exchange Sort - Straight Radix Sort Proiritätswarteschlangen: - Heapsort Mergesort : - Mergesort von Listen - Bottom-Up Mergesort C-Programmierung 2003/2004 245 245 Bubble Sort - Einfaches Sortierverfahren - Sortieren durch direktes Austauschen 99 84 23 11 Austauschen, wenn a>b 84 99 23 11 Austauschen, wenn a>b 84 23 99 11 Austauschen, wenn a>b 84 23 11 99 Ergebnis des 1. Durchlaufes – größte Zahl ist ermittelt 84 23 11 99 Austauschen, wenn a>b 23 84 11 99 Austauschen, wenn a>b 23 11 84 99 Austauschen, wenn a>b 23 11 84 99 Ergebnis des 2. Durchlaufes – zweitgrößte Zahl ermittelt 23 11 84 99 Austauschen, wenn a>b 11 23 84 99 Austauschen, wenn a>b 11 23 84 99 Austauschen, wenn a>b 11 23 84 99 Sortierung fertig C-Programmierung 2003/2004 246 246 99 84 23 11 Austauschen, wenn a>b 84 99 23 11 Austauschen, wenn a>b 84 23 99 11 Austauschen, wenn a>b 84 23 11 99 Ergebnis des 1. Durchlaufes – größte Zahl ist ermittelt 84 23 11 99 Austauschen, wenn a>b 23 84 11 99 Austauschen, wenn a>b 23 11 84 99 Austauschen, wenn a>b 23 11 84 99 Ergebnis des 2. Durchlaufes – zweitgrößte Zahl ermittelt 23 11 84 99 Austauschen, wenn a>b 11 23 84 99 Austauschen, wenn a>b kann entfallen !!! 11 23 84 99 Austauschen, wenn a>b kann entfallen !!! 11 23 84 99 Sortierung fertig kann enffallen !!! - Bei N Elementen braucht man für die Sortierung N-1 Durchläufe in der äußeren Schleife und in jeder inneren Schleife immer einen Durchlauf weniger C-Programmierung 2003/2004 247 247 // Bubble_Sort.cpp : Definiert den Einsprungpunkt für die Konsolenanwendung. #include "stdafx.h" /*void bubble(int *a,int n) {int i,j,hv, z1=n,z=n; for (i=1;i<=n;i++,z1--) { for (j=0,z=z1;z>=1;j++,z--) { if(a[j] > a[j+1]) {hv=a[j]; a[j]=a[j+1];a[j+1]=hv;}; }}}*/ void bubble(int *a,int n) {int i,j,hv; for (i=n;i>=1;i--) for (j=1;j<i;j++) if(a[j-1] > a[j]) {hv=a[j-1]; a[j-1]=a[j];a[j]=hv;}; } int main(int argc, char* argv[]) { int feld[4]={99,23,84,11};int n=4; printf("%i %i %i %i \n", feld[0],feld[1],feld[2],feld[3]); bubble (feld,n); printf("%i %i %i %i \n", feld[0],feld[1],feld[2],feld[3]); return 0; } C-Programmierung 2003/2004 248 248 21.5. Suchalgorithmen Elementare Suchmethoden: - Sequentielle Suche - Binäre Suche Ausgeglichene Bäume: - Top-Down 2-3-4 Bäume - Rot-Schwarz Bäume Hashing - Hash-Funktionen - getrennte Verkettung - lineares Austesten - doppeltes Hashing Digitale Suche Externe Suche - indexsequentieller Zugriff - B-Bäume C-Programmierung 2003/2004 249 249 21.6. Datenkomprimierung - Lauflängencodierung - Kodierung mit variabler Länge - Huffmann Codierung - C-Programmierung 2003/2004 250 250 22. Zusammenfassung C-Programmierung 2003/2004 251 251 Steuerzeichen \a BEL (bell), gibt ein akustisches Signal \b BS (backspace), der Cursor geht eine Position nach links \f FF (formfeed), Seitenvorschub wird ausgelöst \n NL (new line), der Cursor geht zum Anfang der nächsten Zeile \r CR (carriage return), der Cursor geht zum Anfang der aktuellen Zeile \t HAT (horizontal tab), der Cursor geht zur nächsten horizontalen Tabulator position \v VT (vertical tab), der Cursor geht zur nächsten vertikalen Tabulatorposition \" " es wird " ausgegeben \' ' es wird ' ausgegeben \? ? Es wird ein ? ausgegeben \\ \ Es wird ein \ ausgegeben C-Programmierung 2003/2004 252 252 Bindungsstärke der Operatoren Operator Bedeutung Priorität a++ Postinkrement 1 a-- Postdekrement 1 a[b] Index 1 a(b) Funktionsaufruf 1 a.b Zugriff auf Element 1 b->b zeigt auf Element 1 C-Programmierung 2003/2004 253 253 Operator Bedeutung Priorität sizeeof a Größe 2 ++a Präinkrement 2 --a Prädekrement 2 &a Adresse 2 *a Dereferenzierung 2 +a Plus 2 -a Minus 2 ~a NOT Bitweise 2 !a NOT Logisch 2 (Deklaration)(a) Cast-Operator 2 C-Programmierung 2003/2004 254 254 Operator Bedeutung Priorität a*b Multiplikation 3 a/b Division 3 a%b Modulo (Rest) 3 a+b Addition 4 a-b Subtraktion 4 a<<b Linksverschiebung 5 a>>b Rechtsverschiebung 5 a<b kleiner 6 a>b größer 6 a<=b kleiner gleich 6 a<=b größer gleich 6 C-Programmierung 2003/2004 255 255 Operator Bedeutung Priorität a==b gleich 7 a!=b ungleich 7 a&b UND Bitweise 8 a^b exclusiv ODER Bitweise 9 a|b ODER Bitweise 10 a&&b UND Logisch 11 a||b ODER Logisch 12 C-Programmierung 2003/2004 256 256 Operator Bedeutung Priorität a?b:c Bedingung 13 a=b Zuweisung 14 a*=b Multiplikation Zuweisung 14 a/=b Division Zuweisung 14 a%=b Modulo Zuweisung 14 a+=b Addition Zuweisung 14 a-=b Subtraktion Zuweisung 14 a<<=b Linksverschiebung Zuweisung 14 a>>=b Rechtsverschiebung Zuweisung14 a&=b UND Bitweise Zuweisung a^b exclusiv ODER Bitzuweisung 14 a|=b ODER Bitzuweisung 14 a,b Komma 15 14 C-Programmierung 2003/2004 257 257 Formatzeichen Gleitkommazahlen FZ Typ Zahlensystem f double Dezimal *.***** Lf long double Dezimal *.***** e double Dezimal *.***E** Le long double Dezimal *.***E** E double *.***E** LE long double Dezimal *.***E* g double Dezimal mit oder ohne E. Lg long double Dezimal mit oder ohne E. G double Dezimal mit oder ohne E. LG long double Dezimal mit oder ohne E. Dezimal Besonderheit C-Programmierung 2003/2004 258 258 Formatzeichen Strings FZ Typ Besonderheit c char einzelnes Zeichen s char[ ] String mit abschließender 0 % gibt % selbst aus Formatzeichen Zeiger FZ Typ n int * hn short * ln long * P void * Besonderheit C-Programmierung 2003/2004 259 259 Formatzeichen Ganzzahlen FZ Typ Zahlensystem i int Dezimal d int Dezimal hi short Dezimal hd short Dezimal li long Dezimal ld long Dezimal u unsigned int Dezimal hu unsigned short Dezimal lu unsigned long Dezimal C-Programmierung 2003/2004 260 260 Ende C-Programmierung 2003/2004 261 261