Programmiersprachen Einführung in C Teil 2: Prof. Dr. Unser erstes C-Programm int main (int argc, char *argv[]) int i; int sum = 0; for (i = 0; i <= 100; i = i + 1) sum = sum + i * i; printf ("The sum from 0.. 100 is %d\n", sum); 2 1
Unser erstes C-Programm Die Sprache C enthält keine Konstrukte für Ein- oder Ausgabe, da diese jeweils stark vom Betriebssystem abhängen. Daher benötigt jedes C-Programm die jeweilige Standardbibliothek stdio.h int main (int argc, char *argv[]) Jedes C-Programm ist selbst eine Funktion, die von der Laufzeitumgebung (i.d.r dem Betriebssystem) aufgerufen wird. Als Ergebnis gibt diese Funktion eine ganze Zahl zurück, wobei negative Zahlen als Fehlercodes interpretiert werden. 3 Unser erstes C-Programm Mit char *argv[] wird dem Hauptprogramm ein Array von char ( Characters ) übergeben (das main dann aber gar nicht nutzt). Arrays werden in C als Pointer auf einen nullterminierten String von Zeichen dargestellt. argv[] enthält also die Speicherstelle des ersten Zeichens dieses Strings, und der Dereferenzierungsoperator * bewirkt, dass der Inhalt dieser Speicherstelle betrachtet wird, und der ist vom Typ char. 4 2
Unser erstes C-Programm int i; /* Deklaration der Variable i als int */ int sum = 0; /* Deklaration der Variable sum und Definition/Initialisierung mit dem Wert 0 */ 5 Unser erstes C-Programm Kontrollstrukturen: For-Schleife for (i = 0; i <= 100; i = i + 1) Startwert Kontrollbedingung Aktion2 Aktion 1 sum = sum + i * i; Die For-Schleife kann auch weit komplexer eingesetzt werden (vgl. spätere Beispiele) 6 3
Unser erstes C-Programm printf ist eine Funktion aus stdio.h Prototyp: printf(const char *format, ) const char *format: Formatstring, z.b. "The sum from 0.. 100 is %d\n %d : Steuerzeichen % : Escape-Zeichen zur Kennzeichnung von Steuerzeichen d : Ausgabe als Dezimalzahl (int) mit Vorzeichen x : Ausgabe als Hexadezimalzahl (int) ohne Vorzeichen f : Ausgabe als Dezimalzahl (double) in der Form (-)mmm.ddd (Anzahl der d s kann spezifiziert werden, z.b. %f3.7 als mmm.ddddddd) 7 Aufgaben Überprüfen Sie, ob die Argumente der Funktion main irgend einen Einfluss haben auf das Ergebnis. Deklarieren Sie i und sum als char. Was passiert? Geben Sie das Ergebnis in sum als Hexadezimalzahl und als Gleitkommazahl aus. 8 4
Umwandlung Fahrenheit-Celsius int fahr, celsius; int upper = 300, lower = 0, step = 20; fahr = lower; while (fahr <= upper) celsius = 5/9*(fahr-32); printf ( %d\t%d\n", fahr, celsius); fahr = fahr + step; 9 Umwandlung Fahrenheit-Celsius Die Umrechnung von Grad Fahrenheit in Grad Celsius kann nach der Formel C = 5/9( F-32) durchgeführt werden Warum funktioniert das angegebene Programm nicht? Wenn Sie den Fehler gefunden haben: Reicht die erzielte Genauigkeit aus? Wie könnte man sie verbessern? Formulieren Sie das Programm mit Hilfe einer for- Schleife. Erzeugen Sie eine möglichst schöne Ausgabe. 10 5
Umwandlung Fahrenheit-Celsius Das Ganze mit einer for-schleife: int fahr; for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf ( %3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); 11 Umwandlung Fahrenheit-Celsius Und jetzt in besserem Programmierstil: int fahr; const int lower = 0, upper = 300, step = 20; for (fahr = lower; fahr <= upper; fahr = fahr + step) printf ( %3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); 12 6
Ein- und Ausgabe von Zeichen Lies ein Zeichen while (dieses Zeichen ist nicht das Ende-des-Files-Zeichen) gib das gerade gelesene Zeichen wieder aus lies ein Zeichen main () int c; c = getchar(); while (c!= EOF) putchar(c); c = getchar(); 13 Ein- und Ausgabe von Zeichen Eleganter wird es, wenn die Zuweisung c = getchar() innerhalb des Ausdrucks zur Steuerung der while-schleife vorgenommen wird: main () int c; while ((c = getchar())!= EOF) putchar(c); 14 7
Einschub: Semantik der while-schleife Die while-schleife hat folgende allgemeine Struktur: while (Ausdruck) Anweisung; Beim Durchlaufen der while-schleife wird zunächst der Ausdruck ausgewertet. Ergibt diese Auswertung ein Ergebnis 0, so wird dieses Ergebnis als TRUE interpretiert, und Anweisung wird ausgeführt. 15 Ein- und Ausgabe von Zeichen In while ((c = getchar())!= EOF) putchar(c); wird zunächst die Anweisung c = getchar() durchgeführt; ihr Wert ist der Wert der linken Seite, also der Wert von c. Dann wird dieser Wert mit dem Wert EOF verglichen; fällt dieser Vergleich negativ aus, dann erhält der Ausdruck den Wert TRUE, und putchar(c); kann ausgeführt werden. 16 8
Ein- und Ausgabe von Zeichen Warum muss c vom Typ int (2 Byte) sein, obwohl doch nur Zeichen (1 Byte) ein- und ausgegeben werden? Was macht das Programm getchar.c : int c; FILE *fd; fd = fopen("getchar.c","r"); while ((c = getc(fd))!= EOF) putchar(c); 17 Zählen von Zeichen Das folgende Programm countchar.c zählt die Zeichen seines eigenen Quellcodes long nc=0; FILE *fd; fd = fopen("countchar.c","r"); while ((getc(fd))!= EOF) ++nc; printf("%ld\n", nc); 18 9
Zählen von Zeichen Das File, in dem der Quellcode steht, wird im Filesystem des Betriebssystems verwaltet. Das Betriebssystem muss dem Programm daher erst einen Pointer auf dieses File zur Verfügung stellen, sonst kann nicht gezählt werden. fd ist also ein Pointer auf ein Objekt vom Typ FILE: FILE *fd; fopen(char *name, char *mode) gibt also einen Pointer auf das File mit dem angegebenen Namen zurück; r öffnet das File zum Lesen ( read ). fd = fopen( countchar.c","r"); getc(fd) liest jeweils das nächste Zeichen aus dem File, auf das der Pointer fd zeigt. ++nc erhöht den Wert von nc um 1. 19 Zählen von Zeichen Hier die zweite, etwas abgeänderte Version des Programms double nc; FILE *fd; fd = fopen("getchar.cpp","r"); for (nc=0; getc(fd)!= EOF; ++nc) ; printf("%.0f\n", nc); 20 10
Einschub: Semantik der for-schleife for (expr 1 ; expr 2 ; expr 3 ) statement ist äquivalent zu expr 1 ; while (expr 2 ) statement expr 3 ; for (nc=0; getc(fd)!= EOF; ++nc) ; setzt also zunächst nc auf 0 (expr 1 ); dann wird die Bedingung getc(fd)!= EOF überprüft und im Erfolgsfall das statement (hier nur die leere Anweisung, die aus Syntaxgründen da stehen muss) durchgeführt. Zuletzt wird nc um 1 erhöht (expr 3 ). 21 Zählen von Zeichen, Wörtern und Zeilen int c, nl, nw, nc, state; enum state OUT, IN; FILE *fd; fd = fopen( wordcount.c","r"); state = OUT; nl = nw = nc = 0; while ((c = getc(fd))!= EOF) ++nc; if (c == '\n') ++nl; if (c == ' ' c == '\n' c == '\t') state = OUT; else if (state == OUT) state = IN; ++nw; printf("# Zeilen = %d\n# Woerter = %d\n# Zeichen = %d\n", nl, nw, nc); 22 11
Zählen von Zeichen, Wörtern und Zeilen Bei jedem gelesenen Zeichen: ++nc Nach jedem geladenen Newline-Zeichen \n: ++nl Leerzeichen, Tabulator (\t) oder Newline (\n) beenden ein Wort, daher wird state = OUT gesetzt. Wird ein anderes als diese drei Sonderzeichen gelesen, und war das letzte gelesene Zeichen eines dieser Sonderzeichen (state = OUT), so wird jetzt ein neues Wort betreten (das auch aus nur einem Zeichen bestehen kann; state = IN): ++nw 23 Funktionen in C int power(int m, int n); /* Funktionsprototyp: Hilfe f.d. Compiler */ int i; for (i=0; i<10; ++i) printf("i=%d 2^i=%d (-3)^i=%d\n", i, power(2,i), power(-3,i)); int power(int base, int n) int i,p=1; /* Hier ist i lokale Variable */ for (i=1; i<=n; ++i) p=p*base; return p; 24 12
Funktionen in C Funktionen dienen der Strukturierung und sind deshalb so häufig wie möglich anzuwenden. Funktionen sollten als Prototyp deklariert werden; das hilft dem Compiler, Syntaxfehler aufzuspüren. Argumente werden grundsätzlich als Call by Value übergeben; es gibt hier keine Wahlmöglichkeit wir z.b. in PASCAL. (Einziger Ausweg: Pointer.) In Funktionen deklarierte Variablen sind lokal, d.h. sie sind für das aufrufende Programm nicht sichtbar; sie dürfen daher den gleichen Namen haben wie Variablen dieses Programms. 25 Funktionen in C: Call by Value int power(int m, int n); /* Funktionsprototyp: Hilfe f.d. Compiler */ int i; for (i=0; i<10; ++i) printf("i=%d 2^i=%d (-3)^i=%d\n", i, power(2,i), power(-3,i)); int power(int base, int n) int p; for (p=1; n>0; --n) /* Dies hat keinen Einfluss auf i in main */ p=p*base; return p; 26 13
Funktionen in C: Call by Value void swap(int a, int b); int a1=111, a2=222; printf( Vor der Vertauschung: a1=%d, a2=%d.\n, a1, a2); swap(a1,a2); printf( Nach der Vertauschung: a1=%d, a2=%d.\n, a1, a2); void swap(int a, int b) /* Nichts passiert */ int temp; temp = a; a = b; b = temp; 27 Funktionen in C: Call by Value void swap(int *a, int *b); int a1=111, a2=222; printf("vor der Vertauschung: a1=%d, a2=%d.\n", a1, a2); swap(&a1,&a2); printf("nach der Vertauschung: a1=%d, a2=%d.\n", a1, a2); void swap(int *a, int *b) /* Jetzt klappts */ int temp; temp = *a; *a = *b; *b = temp; 28 14
Literatur Kernighan, Ritchie, Programmieren in C, 2. Auflage, Hanser 1990. 29 15